rnd-20070407-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER         0
135 #define GAME_PANEL_GEMS                 1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                 30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                 38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH         41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL          57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       59
194 #define GAME_PANEL_CONVEYOR_BELT_2              60
195 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       61
196 #define GAME_PANEL_CONVEYOR_BELT_3              62
197 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_4              64
199 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       65
200 #define GAME_PANEL_MAGIC_WALL                   66
201 #define GAME_PANEL_MAGIC_WALL_TIME              67
202 #define GAME_PANEL_GRAVITY_STATE                68
203 #define GAME_PANEL_PLAYER_NAME          69
204 #define GAME_PANEL_LEVEL_NAME                   70
205 #define GAME_PANEL_LEVEL_AUTHOR         71
206
207 #define NUM_GAME_PANEL_CONTROLS                 72
208
209 struct GamePanelControlInfo
210 {
211   int nr;
212
213   struct TextPosInfo *pos;
214   int type;
215
216   int value, last_value;
217   int frame, last_frame;
218   int gfx_frame;
219 };
220
221 static struct GamePanelControlInfo game_panel_controls[] =
222 {
223   {
224     GAME_PANEL_LEVEL_NUMBER,
225     &game.panel.level_number,
226     TYPE_INTEGER,
227   },
228   {
229     GAME_PANEL_GEMS,
230     &game.panel.gems,
231     TYPE_INTEGER,
232   },
233   {
234     GAME_PANEL_INVENTORY_COUNT,
235     &game.panel.inventory_count,
236     TYPE_INTEGER,
237   },
238   {
239     GAME_PANEL_INVENTORY_FIRST_1,
240     &game.panel.inventory_first_1,
241     TYPE_ELEMENT,
242   },
243   {
244     GAME_PANEL_INVENTORY_FIRST_2,
245     &game.panel.inventory_first_2,
246     TYPE_ELEMENT,
247   },
248   {
249     GAME_PANEL_INVENTORY_FIRST_3,
250     &game.panel.inventory_first_3,
251     TYPE_ELEMENT,
252   },
253   {
254     GAME_PANEL_INVENTORY_FIRST_4,
255     &game.panel.inventory_first_4,
256     TYPE_ELEMENT,
257   },
258   {
259     GAME_PANEL_INVENTORY_FIRST_5,
260     &game.panel.inventory_first_5,
261     TYPE_ELEMENT,
262   },
263   {
264     GAME_PANEL_INVENTORY_FIRST_6,
265     &game.panel.inventory_first_6,
266     TYPE_ELEMENT,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_7,
270     &game.panel.inventory_first_7,
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_8,
275     &game.panel.inventory_first_8,
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_LAST_1,
280     &game.panel.inventory_last_1,
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_LAST_2,
285     &game.panel.inventory_last_2,
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_LAST_3,
290     &game.panel.inventory_last_3,
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_LAST_4,
295     &game.panel.inventory_last_4,
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_LAST_5,
300     &game.panel.inventory_last_5,
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_LAST_6,
305     &game.panel.inventory_last_6,
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_7,
310     &game.panel.inventory_last_7,
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_8,
315     &game.panel.inventory_last_8,
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_KEY_1,
320     &game.panel.key[0],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_KEY_2,
325     &game.panel.key[1],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_KEY_3,
330     &game.panel.key[2],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_KEY_4,
335     &game.panel.key[3],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_KEY_5,
340     &game.panel.key[4],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_KEY_6,
345     &game.panel.key[5],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_7,
350     &game.panel.key[6],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_8,
355     &game.panel.key[7],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_WHITE,
360     &game.panel.key_white,
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_WHITE_COUNT,
365     &game.panel.key_white_count,
366     TYPE_INTEGER,
367   },
368   {
369     GAME_PANEL_SCORE,
370     &game.panel.score,
371     TYPE_INTEGER,
372   },
373   {
374     GAME_PANEL_TIME,
375     &game.panel.time,
376     TYPE_INTEGER,
377   },
378   {
379     GAME_PANEL_TIME_HH,
380     &game.panel.time_hh,
381     TYPE_INTEGER,
382   },
383   {
384     GAME_PANEL_TIME_MM,
385     &game.panel.time_mm,
386     TYPE_INTEGER,
387   },
388   {
389     GAME_PANEL_TIME_SS,
390     &game.panel.time_ss,
391     TYPE_INTEGER,
392   },
393   {
394     GAME_PANEL_SHIELD_NORMAL,
395     &game.panel.shield_normal,
396     TYPE_ELEMENT,
397   },
398   {
399     GAME_PANEL_SHIELD_NORMAL_TIME,
400     &game.panel.shield_normal_time,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_SHIELD_DEADLY,
405     &game.panel.shield_deadly,
406     TYPE_ELEMENT,
407   },
408   {
409     GAME_PANEL_SHIELD_DEADLY_TIME,
410     &game.panel.shield_deadly_time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_EXIT,
415     &game.panel.exit,
416     TYPE_ELEMENT,
417   },
418   {
419     GAME_PANEL_EMC_MAGIC_BALL,
420     &game.panel.emc_magic_ball,
421     TYPE_ELEMENT,
422   },
423   {
424     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
425     &game.panel.emc_magic_ball_switch,
426     TYPE_ELEMENT,
427   },
428   {
429     GAME_PANEL_LIGHT_SWITCH,
430     &game.panel.light_switch,
431     TYPE_ELEMENT,
432   },
433   {
434     GAME_PANEL_LIGHT_SWITCH_TIME,
435     &game.panel.light_switch_time,
436     TYPE_INTEGER,
437   },
438   {
439     GAME_PANEL_TIMEGATE_SWITCH,
440     &game.panel.timegate_switch,
441     TYPE_ELEMENT,
442   },
443   {
444     GAME_PANEL_TIMEGATE_SWITCH_TIME,
445     &game.panel.timegate_switch_time,
446     TYPE_INTEGER,
447   },
448   {
449     GAME_PANEL_SWITCHGATE_SWITCH,
450     &game.panel.switchgate_switch,
451     TYPE_ELEMENT,
452   },
453   {
454     GAME_PANEL_EMC_LENSES,
455     &game.panel.emc_lenses,
456     TYPE_ELEMENT,
457   },
458   {
459     GAME_PANEL_EMC_LENSES_TIME,
460     &game.panel.emc_lenses_time,
461     TYPE_INTEGER,
462   },
463   {
464     GAME_PANEL_EMC_MAGNIFIER,
465     &game.panel.emc_magnifier,
466     TYPE_ELEMENT,
467   },
468   {
469     GAME_PANEL_EMC_MAGNIFIER_TIME,
470     &game.panel.emc_magnifier_time,
471     TYPE_INTEGER,
472   },
473   {
474     GAME_PANEL_BALLOON_SWITCH,
475     &game.panel.balloon_switch,
476     TYPE_ELEMENT,
477   },
478   {
479     GAME_PANEL_DYNABOMB_NUMBER,
480     &game.panel.dynabomb_number,
481     TYPE_INTEGER,
482   },
483   {
484     GAME_PANEL_DYNABOMB_SIZE,
485     &game.panel.dynabomb_size,
486     TYPE_INTEGER,
487   },
488   {
489     GAME_PANEL_DYNABOMB_POWER,
490     &game.panel.dynabomb_power,
491     TYPE_ELEMENT,
492   },
493   {
494     GAME_PANEL_PENGUINS,
495     &game.panel.penguins,
496     TYPE_INTEGER,
497   },
498   {
499     GAME_PANEL_SOKOBAN_OBJECTS,
500     &game.panel.sokoban_objects,
501     TYPE_INTEGER,
502   },
503   {
504     GAME_PANEL_SOKOBAN_FIELDS,
505     &game.panel.sokoban_fields,
506     TYPE_INTEGER,
507   },
508   {
509     GAME_PANEL_ROBOT_WHEEL,
510     &game.panel.robot_wheel,
511     TYPE_ELEMENT,
512   },
513   {
514     GAME_PANEL_CONVEYOR_BELT_1,
515     &game.panel.conveyor_belt_1,
516     TYPE_ELEMENT,
517   },
518   {
519     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
520     &game.panel.conveyor_belt_1_switch,
521     TYPE_ELEMENT,
522   },
523   {
524     GAME_PANEL_CONVEYOR_BELT_2,
525     &game.panel.conveyor_belt_2,
526     TYPE_ELEMENT,
527   },
528   {
529     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
530     &game.panel.conveyor_belt_2_switch,
531     TYPE_ELEMENT,
532   },
533   {
534     GAME_PANEL_CONVEYOR_BELT_3,
535     &game.panel.conveyor_belt_3,
536     TYPE_ELEMENT,
537   },
538   {
539     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
540     &game.panel.conveyor_belt_3_switch,
541     TYPE_ELEMENT,
542   },
543   {
544     GAME_PANEL_CONVEYOR_BELT_4,
545     &game.panel.conveyor_belt_4,
546     TYPE_ELEMENT,
547   },
548   {
549     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
550     &game.panel.conveyor_belt_4_switch,
551     TYPE_ELEMENT,
552   },
553   {
554     GAME_PANEL_MAGIC_WALL,
555     &game.panel.magic_wall,
556     TYPE_ELEMENT,
557   },
558   {
559     GAME_PANEL_MAGIC_WALL_TIME,
560     &game.panel.magic_wall_time,
561     TYPE_INTEGER,
562   },
563   {
564     GAME_PANEL_GRAVITY_STATE,
565     &game.panel.gravity_state,
566     TYPE_STRING,
567   },
568   {
569     GAME_PANEL_PLAYER_NAME,
570     &game.panel.player_name,
571     TYPE_STRING,
572   },
573   {
574     GAME_PANEL_LEVEL_NAME,
575     &game.panel.level_name,
576     TYPE_STRING,
577   },
578   {
579     GAME_PANEL_LEVEL_AUTHOR,
580     &game.panel.level_author,
581     TYPE_STRING,
582   },
583
584   {
585     -1,
586     NULL,
587     -1,
588   }
589 };
590 #endif
591
592
593 /* values for delayed check of falling and moving elements and for collision */
594 #define CHECK_DELAY_MOVING      3
595 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
596 #define CHECK_DELAY_COLLISION   2
597 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
598
599 /* values for initial player move delay (initial delay counter value) */
600 #define INITIAL_MOVE_DELAY_OFF  -1
601 #define INITIAL_MOVE_DELAY_ON   0
602
603 /* values for player movement speed (which is in fact a delay value) */
604 #define MOVE_DELAY_MIN_SPEED    32
605 #define MOVE_DELAY_NORMAL_SPEED 8
606 #define MOVE_DELAY_HIGH_SPEED   4
607 #define MOVE_DELAY_MAX_SPEED    1
608
609 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
610 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
611
612 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
613 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
614
615 /* values for other actions */
616 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
617 #define MOVE_STEPSIZE_MIN       (1)
618 #define MOVE_STEPSIZE_MAX       (TILEX)
619
620 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
621 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
622
623 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
624
625 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
626                                  RND(element_info[e].push_delay_random))
627 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
628                                  RND(element_info[e].drop_delay_random))
629 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
630                                  RND(element_info[e].move_delay_random))
631 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
632                                     (element_info[e].move_delay_random))
633 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
634                                  RND(element_info[e].ce_value_random_initial))
635 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
636 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
637                                  RND((c)->delay_random * (c)->delay_frames))
638 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
639                                  RND((c)->delay_random))
640
641
642 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
643          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
644
645 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
646         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
647          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
648          (be) + (e) - EL_SELF)
649
650 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
651         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
652          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
653          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
654          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
655          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
656          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
657          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
658          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
659          (e))
660
661 #define CAN_GROW_INTO(e)                                                \
662         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
663
664 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
665                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
666                                         (condition)))
667
668 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
669                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
670                                         (CAN_MOVE_INTO_ACID(e) &&       \
671                                          Feld[x][y] == EL_ACID) ||      \
672                                         (condition)))
673
674 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
675                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
676                                         (CAN_MOVE_INTO_ACID(e) &&       \
677                                          Feld[x][y] == EL_ACID) ||      \
678                                         (condition)))
679
680 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
681                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
682                                         (condition) ||                  \
683                                         (CAN_MOVE_INTO_ACID(e) &&       \
684                                          Feld[x][y] == EL_ACID) ||      \
685                                         (DONT_COLLIDE_WITH(e) &&        \
686                                          IS_PLAYER(x, y) &&             \
687                                          !PLAYER_ENEMY_PROTECTED(x, y))))
688
689 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
690         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
691
692 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
693         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
694
695 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
696         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
697
698 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
699         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
700                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
701
702 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
703         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
704
705 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
706         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
707
708 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
709         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
710
711 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
712         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
713
714 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
715         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
716
717 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
718         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
719                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
720                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
721                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
722                                                  IS_FOOD_PENGUIN(Feld[x][y])))
723 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
724         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
725
726 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
727         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
728
729 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
730         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
731
732 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
733         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
734                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
735
736 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
737
738 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
739                 (!IS_PLAYER(x, y) &&                                    \
740                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
741
742 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
743         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
744
745 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
746 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
747
748 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
749 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
750 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
751 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
752
753 /* game button identifiers */
754 #define GAME_CTRL_ID_STOP               0
755 #define GAME_CTRL_ID_PAUSE              1
756 #define GAME_CTRL_ID_PLAY               2
757 #define SOUND_CTRL_ID_MUSIC             3
758 #define SOUND_CTRL_ID_LOOPS             4
759 #define SOUND_CTRL_ID_SIMPLE            5
760
761 #define NUM_GAME_BUTTONS                6
762
763
764 /* forward declaration for internal use */
765
766 static void CreateField(int, int, int);
767
768 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
769 static void AdvanceFrameAndPlayerCounters(int);
770
771 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
772 static boolean MovePlayer(struct PlayerInfo *, int, int);
773 static void ScrollPlayer(struct PlayerInfo *, int);
774 static void ScrollScreen(struct PlayerInfo *, int);
775
776 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
777
778 static void InitBeltMovement(void);
779 static void CloseAllOpenTimegates(void);
780 static void CheckGravityMovement(struct PlayerInfo *);
781 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
782 static void KillPlayerUnlessEnemyProtected(int, int);
783 static void KillPlayerUnlessExplosionProtected(int, int);
784
785 static void TestIfPlayerTouchesCustomElement(int, int);
786 static void TestIfElementTouchesCustomElement(int, int);
787 static void TestIfElementHitsCustomElement(int, int, int);
788 #if 0
789 static void TestIfElementSmashesCustomElement(int, int, int);
790 #endif
791
792 static void HandleElementChange(int, int, int);
793 static void ExecuteCustomElementAction(int, int, int, int);
794 static boolean ChangeElement(int, int, int, int);
795
796 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
797 #define CheckTriggeredElementChange(x, y, e, ev)                        \
798         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
799 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
800         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
801 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
802         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
803 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
804         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
805
806 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
807 #define CheckElementChange(x, y, e, te, ev)                             \
808         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
809 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
810         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
811 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
812         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
813
814 static void PlayLevelSound(int, int, int);
815 static void PlayLevelSoundNearest(int, int, int);
816 static void PlayLevelSoundAction(int, int, int);
817 static void PlayLevelSoundElementAction(int, int, int, int);
818 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
819 static void PlayLevelSoundActionIfLoop(int, int, int);
820 static void StopLevelSoundActionIfLoop(int, int, int);
821 static void PlayLevelMusic();
822
823 static void MapGameButtons();
824 static void HandleGameButtons(struct GadgetInfo *);
825
826 int AmoebeNachbarNr(int, int);
827 void AmoebeUmwandeln(int, int);
828 void ContinueMoving(int, int);
829 void Bang(int, int);
830 void InitMovDir(int, int);
831 void InitAmoebaNr(int, int);
832 int NewHiScore(void);
833
834 void TestIfGoodThingHitsBadThing(int, int, int);
835 void TestIfBadThingHitsGoodThing(int, int, int);
836 void TestIfPlayerTouchesBadThing(int, int);
837 void TestIfPlayerRunsIntoBadThing(int, int, int);
838 void TestIfBadThingTouchesPlayer(int, int);
839 void TestIfBadThingRunsIntoPlayer(int, int, int);
840 void TestIfFriendTouchesBadThing(int, int);
841 void TestIfBadThingTouchesFriend(int, int);
842 void TestIfBadThingTouchesOtherBadThing(int, int);
843
844 void KillPlayer(struct PlayerInfo *);
845 void BuryPlayer(struct PlayerInfo *);
846 void RemovePlayer(struct PlayerInfo *);
847
848 boolean SnapField(struct PlayerInfo *, int, int);
849 boolean DropElement(struct PlayerInfo *);
850
851 static int getInvisibleActiveFromInvisibleElement(int);
852 static int getInvisibleFromInvisibleActiveElement(int);
853
854 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
855
856 /* for detection of endless loops, caused by custom element programming */
857 /* (using maximal playfield width x 10 is just a rough approximation) */
858 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
859
860 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
861 {                                                                       \
862   if (recursion_loop_detected)                                          \
863     return (rc);                                                        \
864                                                                         \
865   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
866   {                                                                     \
867     recursion_loop_detected = TRUE;                                     \
868     recursion_loop_element = (e);                                       \
869   }                                                                     \
870                                                                         \
871   recursion_loop_depth++;                                               \
872 }
873
874 #define RECURSION_LOOP_DETECTION_END()                                  \
875 {                                                                       \
876   recursion_loop_depth--;                                               \
877 }
878
879 static int recursion_loop_depth;
880 static boolean recursion_loop_detected;
881 static boolean recursion_loop_element;
882
883
884 /* ------------------------------------------------------------------------- */
885 /* definition of elements that automatically change to other elements after  */
886 /* a specified time, eventually calling a function when changing             */
887 /* ------------------------------------------------------------------------- */
888
889 /* forward declaration for changer functions */
890 static void InitBuggyBase(int, int);
891 static void WarnBuggyBase(int, int);
892
893 static void InitTrap(int, int);
894 static void ActivateTrap(int, int);
895 static void ChangeActiveTrap(int, int);
896
897 static void InitRobotWheel(int, int);
898 static void RunRobotWheel(int, int);
899 static void StopRobotWheel(int, int);
900
901 static void InitTimegateWheel(int, int);
902 static void RunTimegateWheel(int, int);
903
904 static void InitMagicBallDelay(int, int);
905 static void ActivateMagicBall(int, int);
906
907 struct ChangingElementInfo
908 {
909   int element;
910   int target_element;
911   int change_delay;
912   void (*pre_change_function)(int x, int y);
913   void (*change_function)(int x, int y);
914   void (*post_change_function)(int x, int y);
915 };
916
917 static struct ChangingElementInfo change_delay_list[] =
918 {
919   {
920     EL_NUT_BREAKING,
921     EL_EMERALD,
922     6,
923     NULL,
924     NULL,
925     NULL
926   },
927   {
928     EL_PEARL_BREAKING,
929     EL_EMPTY,
930     8,
931     NULL,
932     NULL,
933     NULL
934   },
935   {
936     EL_EXIT_OPENING,
937     EL_EXIT_OPEN,
938     29,
939     NULL,
940     NULL,
941     NULL
942   },
943   {
944     EL_EXIT_CLOSING,
945     EL_EXIT_CLOSED,
946     29,
947     NULL,
948     NULL,
949     NULL
950   },
951   {
952     EL_STEEL_EXIT_OPENING,
953     EL_STEEL_EXIT_OPEN,
954     29,
955     NULL,
956     NULL,
957     NULL
958   },
959   {
960     EL_STEEL_EXIT_CLOSING,
961     EL_STEEL_EXIT_CLOSED,
962     29,
963     NULL,
964     NULL,
965     NULL
966   },
967   {
968     EL_EM_EXIT_OPENING,
969     EL_EM_EXIT_OPEN,
970     29,
971     NULL,
972     NULL,
973     NULL
974   },
975   {
976     EL_EM_EXIT_CLOSING,
977 #if 1
978     EL_EMPTY,
979 #else
980     EL_EM_EXIT_CLOSED,
981 #endif
982     29,
983     NULL,
984     NULL,
985     NULL
986   },
987   {
988     EL_EM_STEEL_EXIT_OPENING,
989     EL_EM_STEEL_EXIT_OPEN,
990     29,
991     NULL,
992     NULL,
993     NULL
994   },
995   {
996     EL_EM_STEEL_EXIT_CLOSING,
997 #if 1
998     EL_STEELWALL,
999 #else
1000     EL_EM_STEEL_EXIT_CLOSED,
1001 #endif
1002     29,
1003     NULL,
1004     NULL,
1005     NULL
1006   },
1007   {
1008     EL_SP_EXIT_OPENING,
1009     EL_SP_EXIT_OPEN,
1010     29,
1011     NULL,
1012     NULL,
1013     NULL
1014   },
1015   {
1016     EL_SP_EXIT_CLOSING,
1017     EL_SP_EXIT_CLOSED,
1018     29,
1019     NULL,
1020     NULL,
1021     NULL
1022   },
1023   {
1024     EL_SWITCHGATE_OPENING,
1025     EL_SWITCHGATE_OPEN,
1026     29,
1027     NULL,
1028     NULL,
1029     NULL
1030   },
1031   {
1032     EL_SWITCHGATE_CLOSING,
1033     EL_SWITCHGATE_CLOSED,
1034     29,
1035     NULL,
1036     NULL,
1037     NULL
1038   },
1039   {
1040     EL_TIMEGATE_OPENING,
1041     EL_TIMEGATE_OPEN,
1042     29,
1043     NULL,
1044     NULL,
1045     NULL
1046   },
1047   {
1048     EL_TIMEGATE_CLOSING,
1049     EL_TIMEGATE_CLOSED,
1050     29,
1051     NULL,
1052     NULL,
1053     NULL
1054   },
1055
1056   {
1057     EL_ACID_SPLASH_LEFT,
1058     EL_EMPTY,
1059     8,
1060     NULL,
1061     NULL,
1062     NULL
1063   },
1064   {
1065     EL_ACID_SPLASH_RIGHT,
1066     EL_EMPTY,
1067     8,
1068     NULL,
1069     NULL,
1070     NULL
1071   },
1072   {
1073     EL_SP_BUGGY_BASE,
1074     EL_SP_BUGGY_BASE_ACTIVATING,
1075     0,
1076     InitBuggyBase,
1077     NULL,
1078     NULL
1079   },
1080   {
1081     EL_SP_BUGGY_BASE_ACTIVATING,
1082     EL_SP_BUGGY_BASE_ACTIVE,
1083     0,
1084     InitBuggyBase,
1085     NULL,
1086     NULL
1087   },
1088   {
1089     EL_SP_BUGGY_BASE_ACTIVE,
1090     EL_SP_BUGGY_BASE,
1091     0,
1092     InitBuggyBase,
1093     WarnBuggyBase,
1094     NULL
1095   },
1096   {
1097     EL_TRAP,
1098     EL_TRAP_ACTIVE,
1099     0,
1100     InitTrap,
1101     NULL,
1102     ActivateTrap
1103   },
1104   {
1105     EL_TRAP_ACTIVE,
1106     EL_TRAP,
1107     31,
1108     NULL,
1109     ChangeActiveTrap,
1110     NULL
1111   },
1112   {
1113     EL_ROBOT_WHEEL_ACTIVE,
1114     EL_ROBOT_WHEEL,
1115     0,
1116     InitRobotWheel,
1117     RunRobotWheel,
1118     StopRobotWheel
1119   },
1120   {
1121     EL_TIMEGATE_SWITCH_ACTIVE,
1122     EL_TIMEGATE_SWITCH,
1123     0,
1124     InitTimegateWheel,
1125     RunTimegateWheel,
1126     NULL
1127   },
1128   {
1129     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1130     EL_DC_TIMEGATE_SWITCH,
1131     0,
1132     InitTimegateWheel,
1133     RunTimegateWheel,
1134     NULL
1135   },
1136   {
1137     EL_EMC_MAGIC_BALL_ACTIVE,
1138     EL_EMC_MAGIC_BALL_ACTIVE,
1139     0,
1140     InitMagicBallDelay,
1141     NULL,
1142     ActivateMagicBall
1143   },
1144   {
1145     EL_EMC_SPRING_BUMPER_ACTIVE,
1146     EL_EMC_SPRING_BUMPER,
1147     8,
1148     NULL,
1149     NULL,
1150     NULL
1151   },
1152   {
1153     EL_DIAGONAL_SHRINKING,
1154     EL_UNDEFINED,
1155     0,
1156     NULL,
1157     NULL,
1158     NULL
1159   },
1160   {
1161     EL_DIAGONAL_GROWING,
1162     EL_UNDEFINED,
1163     0,
1164     NULL,
1165     NULL,
1166     NULL,
1167   },
1168
1169   {
1170     EL_UNDEFINED,
1171     EL_UNDEFINED,
1172     -1,
1173     NULL,
1174     NULL,
1175     NULL
1176   }
1177 };
1178
1179 struct
1180 {
1181   int element;
1182   int push_delay_fixed, push_delay_random;
1183 }
1184 push_delay_list[] =
1185 {
1186   { EL_SPRING,                  0, 0 },
1187   { EL_BALLOON,                 0, 0 },
1188
1189   { EL_SOKOBAN_OBJECT,          2, 0 },
1190   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1191   { EL_SATELLITE,               2, 0 },
1192   { EL_SP_DISK_YELLOW,          2, 0 },
1193
1194   { EL_UNDEFINED,               0, 0 },
1195 };
1196
1197 struct
1198 {
1199   int element;
1200   int move_stepsize;
1201 }
1202 move_stepsize_list[] =
1203 {
1204   { EL_AMOEBA_DROP,             2 },
1205   { EL_AMOEBA_DROPPING,         2 },
1206   { EL_QUICKSAND_FILLING,       1 },
1207   { EL_QUICKSAND_EMPTYING,      1 },
1208   { EL_QUICKSAND_FAST_FILLING,  2 },
1209   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1210   { EL_MAGIC_WALL_FILLING,      2 },
1211   { EL_MAGIC_WALL_EMPTYING,     2 },
1212   { EL_BD_MAGIC_WALL_FILLING,   2 },
1213   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1214   { EL_DC_MAGIC_WALL_FILLING,   2 },
1215   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1216
1217   { EL_UNDEFINED,               0 },
1218 };
1219
1220 struct
1221 {
1222   int element;
1223   int count;
1224 }
1225 collect_count_list[] =
1226 {
1227   { EL_EMERALD,                 1 },
1228   { EL_BD_DIAMOND,              1 },
1229   { EL_EMERALD_YELLOW,          1 },
1230   { EL_EMERALD_RED,             1 },
1231   { EL_EMERALD_PURPLE,          1 },
1232   { EL_DIAMOND,                 3 },
1233   { EL_SP_INFOTRON,             1 },
1234   { EL_PEARL,                   5 },
1235   { EL_CRYSTAL,                 8 },
1236
1237   { EL_UNDEFINED,               0 },
1238 };
1239
1240 struct
1241 {
1242   int element;
1243   int direction;
1244 }
1245 access_direction_list[] =
1246 {
1247   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1248   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1249   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1250   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1251   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1252   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1253   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1254   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1255   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1256   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1257   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1258
1259   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1260   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1261   { EL_SP_PORT_UP,                                                   MV_DOWN },
1262   { EL_SP_PORT_DOWN,                                         MV_UP           },
1263   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1264   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1265   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1266   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1267   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1268   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1269   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1270   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1271   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1272   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1273   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1274   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1275   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1276   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1277   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1278
1279   { EL_UNDEFINED,                       MV_NONE                              }
1280 };
1281
1282 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1283
1284 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1285 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1286 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1287                                  IS_JUST_CHANGING(x, y))
1288
1289 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1290
1291 /* static variables for playfield scan mode (scanning forward or backward) */
1292 static int playfield_scan_start_x = 0;
1293 static int playfield_scan_start_y = 0;
1294 static int playfield_scan_delta_x = 1;
1295 static int playfield_scan_delta_y = 1;
1296
1297 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1298                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1299                                      (y) += playfield_scan_delta_y)     \
1300                                 for ((x) = playfield_scan_start_x;      \
1301                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1302                                      (x) += playfield_scan_delta_x)
1303
1304 #ifdef DEBUG
1305 void DEBUG_SetMaximumDynamite()
1306 {
1307   int i;
1308
1309   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1310     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1311       local_player->inventory_element[local_player->inventory_size++] =
1312         EL_DYNAMITE;
1313 }
1314 #endif
1315
1316 static void InitPlayfieldScanModeVars()
1317 {
1318   if (game.use_reverse_scan_direction)
1319   {
1320     playfield_scan_start_x = lev_fieldx - 1;
1321     playfield_scan_start_y = lev_fieldy - 1;
1322
1323     playfield_scan_delta_x = -1;
1324     playfield_scan_delta_y = -1;
1325   }
1326   else
1327   {
1328     playfield_scan_start_x = 0;
1329     playfield_scan_start_y = 0;
1330
1331     playfield_scan_delta_x = 1;
1332     playfield_scan_delta_y = 1;
1333   }
1334 }
1335
1336 static void InitPlayfieldScanMode(int mode)
1337 {
1338   game.use_reverse_scan_direction =
1339     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1340
1341   InitPlayfieldScanModeVars();
1342 }
1343
1344 static int get_move_delay_from_stepsize(int move_stepsize)
1345 {
1346   move_stepsize =
1347     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1348
1349   /* make sure that stepsize value is always a power of 2 */
1350   move_stepsize = (1 << log_2(move_stepsize));
1351
1352   return TILEX / move_stepsize;
1353 }
1354
1355 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1356                                boolean init_game)
1357 {
1358   int player_nr = player->index_nr;
1359   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1360   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1361
1362   /* do no immediately change move delay -- the player might just be moving */
1363   player->move_delay_value_next = move_delay;
1364
1365   /* information if player can move must be set separately */
1366   player->cannot_move = cannot_move;
1367
1368   if (init_game)
1369   {
1370     player->move_delay       = game.initial_move_delay[player_nr];
1371     player->move_delay_value = game.initial_move_delay_value[player_nr];
1372
1373     player->move_delay_value_next = -1;
1374
1375     player->move_delay_reset_counter = 0;
1376   }
1377 }
1378
1379 void GetPlayerConfig()
1380 {
1381   GameFrameDelay = setup.game_frame_delay;
1382
1383   if (!audio.sound_available)
1384     setup.sound_simple = FALSE;
1385
1386   if (!audio.loops_available)
1387     setup.sound_loops = FALSE;
1388
1389   if (!audio.music_available)
1390     setup.sound_music = FALSE;
1391
1392   if (!video.fullscreen_available)
1393     setup.fullscreen = FALSE;
1394
1395   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1396
1397   SetAudioMode(setup.sound);
1398   InitJoysticks();
1399 }
1400
1401 int GetElementFromGroupElement(int element)
1402 {
1403   if (IS_GROUP_ELEMENT(element))
1404   {
1405     struct ElementGroupInfo *group = element_info[element].group;
1406     int last_anim_random_frame = gfx.anim_random_frame;
1407     int element_pos;
1408
1409     if (group->choice_mode == ANIM_RANDOM)
1410       gfx.anim_random_frame = RND(group->num_elements_resolved);
1411
1412     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1413                                     group->choice_mode, 0,
1414                                     group->choice_pos);
1415
1416     if (group->choice_mode == ANIM_RANDOM)
1417       gfx.anim_random_frame = last_anim_random_frame;
1418
1419     group->choice_pos++;
1420
1421     element = group->element_resolved[element_pos];
1422   }
1423
1424   return element;
1425 }
1426
1427 static void InitPlayerField(int x, int y, int element, boolean init_game)
1428 {
1429   if (element == EL_SP_MURPHY)
1430   {
1431     if (init_game)
1432     {
1433       if (stored_player[0].present)
1434       {
1435         Feld[x][y] = EL_SP_MURPHY_CLONE;
1436
1437         return;
1438       }
1439       else
1440       {
1441         stored_player[0].use_murphy = TRUE;
1442
1443         if (!level.use_artwork_element[0])
1444           stored_player[0].artwork_element = EL_SP_MURPHY;
1445       }
1446
1447       Feld[x][y] = EL_PLAYER_1;
1448     }
1449   }
1450
1451   if (init_game)
1452   {
1453     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1454     int jx = player->jx, jy = player->jy;
1455
1456     player->present = TRUE;
1457
1458     player->block_last_field = (element == EL_SP_MURPHY ?
1459                                 level.sp_block_last_field :
1460                                 level.block_last_field);
1461
1462     /* ---------- initialize player's last field block delay --------------- */
1463
1464     /* always start with reliable default value (no adjustment needed) */
1465     player->block_delay_adjustment = 0;
1466
1467     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1468     if (player->block_last_field && element == EL_SP_MURPHY)
1469       player->block_delay_adjustment = 1;
1470
1471     /* special case 2: in game engines before 3.1.1, blocking was different */
1472     if (game.use_block_last_field_bug)
1473       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1474
1475     if (!options.network || player->connected)
1476     {
1477       player->active = TRUE;
1478
1479       /* remove potentially duplicate players */
1480       if (StorePlayer[jx][jy] == Feld[x][y])
1481         StorePlayer[jx][jy] = 0;
1482
1483       StorePlayer[x][y] = Feld[x][y];
1484
1485       if (options.debug)
1486       {
1487         printf("Player %d activated.\n", player->element_nr);
1488         printf("[Local player is %d and currently %s.]\n",
1489                local_player->element_nr,
1490                local_player->active ? "active" : "not active");
1491       }
1492     }
1493
1494     Feld[x][y] = EL_EMPTY;
1495
1496     player->jx = player->last_jx = x;
1497     player->jy = player->last_jy = y;
1498   }
1499 }
1500
1501 static void InitField(int x, int y, boolean init_game)
1502 {
1503   int element = Feld[x][y];
1504
1505   switch (element)
1506   {
1507     case EL_SP_MURPHY:
1508     case EL_PLAYER_1:
1509     case EL_PLAYER_2:
1510     case EL_PLAYER_3:
1511     case EL_PLAYER_4:
1512       InitPlayerField(x, y, element, init_game);
1513       break;
1514
1515     case EL_SOKOBAN_FIELD_PLAYER:
1516       element = Feld[x][y] = EL_PLAYER_1;
1517       InitField(x, y, init_game);
1518
1519       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1520       InitField(x, y, init_game);
1521       break;
1522
1523     case EL_SOKOBAN_FIELD_EMPTY:
1524       local_player->sokobanfields_still_needed++;
1525       break;
1526
1527     case EL_STONEBLOCK:
1528       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1529         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1530       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1531         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1532       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1533         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1534       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1535         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1536       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1537         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1538       break;
1539
1540     case EL_BUG:
1541     case EL_BUG_RIGHT:
1542     case EL_BUG_UP:
1543     case EL_BUG_LEFT:
1544     case EL_BUG_DOWN:
1545     case EL_SPACESHIP:
1546     case EL_SPACESHIP_RIGHT:
1547     case EL_SPACESHIP_UP:
1548     case EL_SPACESHIP_LEFT:
1549     case EL_SPACESHIP_DOWN:
1550     case EL_BD_BUTTERFLY:
1551     case EL_BD_BUTTERFLY_RIGHT:
1552     case EL_BD_BUTTERFLY_UP:
1553     case EL_BD_BUTTERFLY_LEFT:
1554     case EL_BD_BUTTERFLY_DOWN:
1555     case EL_BD_FIREFLY:
1556     case EL_BD_FIREFLY_RIGHT:
1557     case EL_BD_FIREFLY_UP:
1558     case EL_BD_FIREFLY_LEFT:
1559     case EL_BD_FIREFLY_DOWN:
1560     case EL_PACMAN_RIGHT:
1561     case EL_PACMAN_UP:
1562     case EL_PACMAN_LEFT:
1563     case EL_PACMAN_DOWN:
1564     case EL_YAMYAM:
1565     case EL_YAMYAM_LEFT:
1566     case EL_YAMYAM_RIGHT:
1567     case EL_YAMYAM_UP:
1568     case EL_YAMYAM_DOWN:
1569     case EL_DARK_YAMYAM:
1570     case EL_ROBOT:
1571     case EL_PACMAN:
1572     case EL_SP_SNIKSNAK:
1573     case EL_SP_ELECTRON:
1574     case EL_MOLE:
1575     case EL_MOLE_LEFT:
1576     case EL_MOLE_RIGHT:
1577     case EL_MOLE_UP:
1578     case EL_MOLE_DOWN:
1579       InitMovDir(x, y);
1580       break;
1581
1582     case EL_AMOEBA_FULL:
1583     case EL_BD_AMOEBA:
1584       InitAmoebaNr(x, y);
1585       break;
1586
1587     case EL_AMOEBA_DROP:
1588       if (y == lev_fieldy - 1)
1589       {
1590         Feld[x][y] = EL_AMOEBA_GROWING;
1591         Store[x][y] = EL_AMOEBA_WET;
1592       }
1593       break;
1594
1595     case EL_DYNAMITE_ACTIVE:
1596     case EL_SP_DISK_RED_ACTIVE:
1597     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1598     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1599     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1600     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1601       MovDelay[x][y] = 96;
1602       break;
1603
1604     case EL_EM_DYNAMITE_ACTIVE:
1605       MovDelay[x][y] = 32;
1606       break;
1607
1608     case EL_LAMP:
1609       local_player->lights_still_needed++;
1610       break;
1611
1612     case EL_PENGUIN:
1613       local_player->friends_still_needed++;
1614       break;
1615
1616     case EL_PIG:
1617     case EL_DRAGON:
1618       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1619       break;
1620
1621     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1622     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1623     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1624     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1625     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1626     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1627     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1628     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1629     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1630     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1631     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1632     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1633       if (init_game)
1634       {
1635         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1636         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1637         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1638
1639         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1640         {
1641           game.belt_dir[belt_nr] = belt_dir;
1642           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1643         }
1644         else    /* more than one switch -- set it like the first switch */
1645         {
1646           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1647         }
1648       }
1649       break;
1650
1651 #if !USE_BOTH_SWITCHGATE_SWITCHES
1652     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1653       if (init_game)
1654         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1655       break;
1656
1657     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1658       if (init_game)
1659         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1660       break;
1661 #endif
1662
1663     case EL_LIGHT_SWITCH_ACTIVE:
1664       if (init_game)
1665         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1666       break;
1667
1668     case EL_INVISIBLE_STEELWALL:
1669     case EL_INVISIBLE_WALL:
1670     case EL_INVISIBLE_SAND:
1671       if (game.light_time_left > 0 ||
1672           game.lenses_time_left > 0)
1673         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1674       break;
1675
1676     case EL_EMC_MAGIC_BALL:
1677       if (game.ball_state)
1678         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1679       break;
1680
1681     case EL_EMC_MAGIC_BALL_SWITCH:
1682       if (game.ball_state)
1683         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1684       break;
1685
1686     default:
1687       if (IS_CUSTOM_ELEMENT(element))
1688       {
1689         if (CAN_MOVE(element))
1690           InitMovDir(x, y);
1691
1692 #if USE_NEW_CUSTOM_VALUE
1693         if (!element_info[element].use_last_ce_value || init_game)
1694           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1695 #endif
1696       }
1697       else if (IS_GROUP_ELEMENT(element))
1698       {
1699         Feld[x][y] = GetElementFromGroupElement(element);
1700
1701         InitField(x, y, init_game);
1702       }
1703
1704       break;
1705   }
1706
1707   if (!init_game)
1708     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1709 }
1710
1711 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1712 {
1713   InitField(x, y, init_game);
1714
1715   /* not needed to call InitMovDir() -- already done by InitField()! */
1716   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1717       CAN_MOVE(Feld[x][y]))
1718     InitMovDir(x, y);
1719 }
1720
1721 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1722 {
1723   int old_element = Feld[x][y];
1724
1725   InitField(x, y, init_game);
1726
1727   /* not needed to call InitMovDir() -- already done by InitField()! */
1728   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1729       CAN_MOVE(old_element) &&
1730       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1731     InitMovDir(x, y);
1732
1733   /* this case is in fact a combination of not less than three bugs:
1734      first, it calls InitMovDir() for elements that can move, although this is
1735      already done by InitField(); then, it checks the element that was at this
1736      field _before_ the call to InitField() (which can change it); lastly, it
1737      was not called for "mole with direction" elements, which were treated as
1738      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1739   */
1740 }
1741
1742 #if 1
1743
1744 static int get_key_element_from_nr(int key_nr)
1745 {
1746   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1747                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1748                           EL_EM_KEY_1 : EL_KEY_1);
1749
1750   return key_base_element + key_nr;
1751 }
1752
1753 static int get_next_dropped_element(struct PlayerInfo *player)
1754 {
1755   return (player->inventory_size > 0 ?
1756           player->inventory_element[player->inventory_size - 1] :
1757           player->inventory_infinite_element != EL_UNDEFINED ?
1758           player->inventory_infinite_element :
1759           player->dynabombs_left > 0 ?
1760           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1761           EL_UNDEFINED);
1762 }
1763
1764 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1765 {
1766   /* pos >= 0: get element from bottom of the stack;
1767      pos <  0: get element from top of the stack */
1768
1769   if (pos < 0)
1770   {
1771     int min_inventory_size = -pos;
1772     int inventory_pos = player->inventory_size - min_inventory_size;
1773     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1774
1775     return (player->inventory_size >= min_inventory_size ?
1776             player->inventory_element[inventory_pos] :
1777             player->inventory_infinite_element != EL_UNDEFINED ?
1778             player->inventory_infinite_element :
1779             player->dynabombs_left >= min_dynabombs_left ?
1780             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1781             EL_UNDEFINED);
1782   }
1783   else
1784   {
1785     int min_dynabombs_left = pos + 1;
1786     int min_inventory_size = pos + 1 - player->dynabombs_left;
1787     int inventory_pos = pos - player->dynabombs_left;
1788
1789     return (player->inventory_infinite_element != EL_UNDEFINED ?
1790             player->inventory_infinite_element :
1791             player->dynabombs_left >= min_dynabombs_left ?
1792             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1793             player->inventory_size >= min_inventory_size ?
1794             player->inventory_element[inventory_pos] :
1795             EL_UNDEFINED);
1796   }
1797 }
1798
1799 void InitGameControlValues()
1800 {
1801   int i;
1802
1803   for (i = 0; game_panel_controls[i].nr != -1; i++)
1804   {
1805     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
1806     int nr = gpc->nr;
1807     int type = gpc->type;
1808     struct TextPosInfo *pos = gpc->pos;
1809
1810     if (nr != i)
1811     {
1812       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
1813       Error(ERR_EXIT, "this should not happen -- please debug");
1814     }
1815
1816     /* force update of game controls after initialization */
1817     gpc->value = gpc->last_value = -1;
1818     gpc->frame = gpc->last_frame = -1;
1819     gpc->gfx_frame = -1;
1820
1821     /* determine panel value width for later calculation of alignment */
1822     if (type == TYPE_INTEGER || type == TYPE_STRING)
1823     {
1824       pos->width = pos->size * getFontWidth(pos->font);
1825       pos->height = getFontHeight(pos->font);
1826     }
1827     else if (type == TYPE_ELEMENT)
1828     {
1829       pos->width = pos->size;
1830       pos->height = pos->size;
1831     }
1832   }
1833 }
1834
1835 void UpdateGameControlValues()
1836 {
1837   int i, k;
1838   int time = (level.time == 0 ? TimePlayed : TimeLeft);
1839   int score = (local_player->LevelSolved ? local_player->score_final :
1840                local_player->score);
1841   int exit_closed = (local_player->gems_still_needed > 0 ||
1842                      local_player->sokobanfields_still_needed > 0 ||
1843                      local_player->lights_still_needed > 0);
1844
1845   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
1846   game_panel_controls[GAME_PANEL_GEMS].value =
1847     local_player->gems_still_needed;
1848
1849   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
1850   for (i = 0; i < MAX_NUM_KEYS; i++)
1851     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
1852   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
1853   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
1854
1855   if (game.centered_player_nr == -1)
1856   {
1857     for (i = 0; i < MAX_PLAYERS; i++)
1858     {
1859       for (k = 0; k < MAX_NUM_KEYS; k++)
1860         if (stored_player[i].key[k])
1861           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1862             get_key_element_from_nr(k);
1863
1864       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1865         stored_player[i].inventory_size;
1866
1867       if (stored_player[i].num_white_keys > 0)
1868         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
1869           EL_DC_KEY_WHITE;
1870
1871       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1872         stored_player[i].num_white_keys;
1873     }
1874   }
1875   else
1876   {
1877     int player_nr = game.centered_player_nr;
1878
1879     for (k = 0; k < MAX_NUM_KEYS; k++)
1880       if (stored_player[player_nr].key[k])
1881         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1882           get_key_element_from_nr(k);
1883
1884     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1885       stored_player[player_nr].inventory_size;
1886
1887     if (stored_player[player_nr].num_white_keys > 0)
1888       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
1889
1890     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1891       stored_player[player_nr].num_white_keys;
1892   }
1893
1894   for (i = 0; i < 8; i++)
1895   {
1896     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
1897       get_inventory_element_from_pos(local_player, i);
1898     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
1899       get_inventory_element_from_pos(local_player, -i - 1);
1900   }
1901
1902   game_panel_controls[GAME_PANEL_SCORE].value = score;
1903
1904   game_panel_controls[GAME_PANEL_TIME].value = time;
1905
1906   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
1907   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
1908   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
1909
1910   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
1911     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
1912      EL_EMPTY);
1913   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
1914     local_player->shield_normal_time_left;
1915   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
1916     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
1917      EL_EMPTY);
1918   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
1919     local_player->shield_deadly_time_left;
1920
1921   game_panel_controls[GAME_PANEL_EXIT].value =
1922     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
1923
1924   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
1925     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
1926   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
1927     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
1928      EL_EMC_MAGIC_BALL_SWITCH);
1929
1930   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
1931     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
1932   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
1933     game.light_time_left;
1934
1935   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
1936     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
1937   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
1938     game.timegate_time_left;
1939
1940   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
1941     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
1942
1943   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
1944     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
1945   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
1946     game.lenses_time_left;
1947
1948   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
1949     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
1950   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
1951     game.magnify_time_left;
1952
1953   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
1954     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
1955      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
1956      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
1957      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
1958      EL_BALLOON_SWITCH_NONE);
1959
1960   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
1961     local_player->dynabomb_count;
1962   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
1963     local_player->dynabomb_size;
1964   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
1965     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
1966
1967   game_panel_controls[GAME_PANEL_PENGUINS].value =
1968     local_player->friends_still_needed;
1969
1970   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
1971     local_player->sokobanfields_still_needed;
1972   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
1973     local_player->sokobanfields_still_needed;
1974
1975   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
1976     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
1977
1978   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1].value =
1979     (game.belt_dir[0] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
1980      EL_CONVEYOR_BELT_1_MIDDLE);
1981   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH].value =
1982     getBeltSwitchElementFromBeltNrAndBeltDir(0, game.belt_dir[0]);
1983   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_2].value =
1984     (game.belt_dir[1] != MV_NONE ? EL_CONVEYOR_BELT_2_MIDDLE_ACTIVE :
1985      EL_CONVEYOR_BELT_2_MIDDLE);
1986   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_2_SWITCH].value =
1987     getBeltSwitchElementFromBeltNrAndBeltDir(1, game.belt_dir[1]);
1988   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_3].value =
1989     (game.belt_dir[2] != MV_NONE ? EL_CONVEYOR_BELT_3_MIDDLE_ACTIVE :
1990      EL_CONVEYOR_BELT_3_MIDDLE);
1991   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_3_SWITCH].value =
1992     getBeltSwitchElementFromBeltNrAndBeltDir(2, game.belt_dir[2]);
1993   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_4].value =
1994     (game.belt_dir[3] != MV_NONE ? EL_CONVEYOR_BELT_4_MIDDLE_ACTIVE :
1995      EL_CONVEYOR_BELT_4_MIDDLE);
1996   game_panel_controls[GAME_PANEL_CONVEYOR_BELT_4_SWITCH].value =
1997     getBeltSwitchElementFromBeltNrAndBeltDir(3, game.belt_dir[3]);
1998
1999   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2000     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2001   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2002     game.magic_wall_time_left;
2003
2004 #if USE_PLAYER_GRAVITY
2005   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2006     local_player->gravity;
2007 #else
2008   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2009 #endif
2010
2011   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2012   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2013   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2014
2015   for (i = 0; game_panel_controls[i].nr != -1; i++)
2016   {
2017     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2018
2019     if (gpc->type == TYPE_ELEMENT)
2020     {
2021       if (gpc->value != gpc->last_value)
2022         gpc->gfx_frame = 0;
2023       else
2024         gpc->gfx_frame++;
2025
2026       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2027                                             gpc->gfx_frame);
2028     }
2029   }
2030 }
2031
2032 void DisplayGameControlValues()
2033 {
2034   int i;
2035
2036   game_status = GAME_MODE_PSEUDO_PANEL;
2037
2038   for (i = 0; game_panel_controls[i].nr != -1; i++)
2039   {
2040     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2041     int nr = gpc->nr;
2042     int type = gpc->type;
2043     struct TextPosInfo *pos = gpc->pos;
2044     int value = gpc->value;
2045     int frame = gpc->frame;
2046     int last_value = gpc->last_value;
2047     int last_frame = gpc->last_frame;
2048     int size = pos->size;
2049     int font = pos->font;
2050
2051     if (value == last_value && frame == last_frame)
2052       continue;
2053
2054     gpc->last_value = value;
2055     gpc->last_frame = frame;
2056
2057 #if 0
2058     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2059 #endif
2060
2061     if (PANEL_DEACTIVATED(pos))
2062       continue;
2063
2064     if (type == TYPE_INTEGER)
2065     {
2066       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2067           nr == GAME_PANEL_TIME)
2068       {
2069         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2070
2071         if (use_dynamic_size)           /* use dynamic number of digits */
2072         {
2073           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2074           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2075           int size2 = size1 + 1;
2076           int font1 = pos->font;
2077           int font2 = pos->font_alt;
2078
2079           size = (value < value_change ? size1 : size2);
2080           font = (value < value_change ? font1 : font2);
2081
2082           /* clear background if value just changed its size (dynamic digits) */
2083           if ((last_value < value_change) != (value < value_change))
2084           {
2085             int width1 = size1 * getFontWidth(font1);
2086             int width2 = size2 * getFontWidth(font2);
2087             int max_width = MAX(width1, width2);
2088             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2089
2090             pos->width = max_width;
2091
2092             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2093                                        max_width, max_height);
2094           }
2095         }
2096
2097         pos->width = size * getFontWidth(font);
2098       }
2099
2100       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2101     }
2102     else if (type == TYPE_ELEMENT)
2103     {
2104       int dst_x = PANEL_XPOS(pos);
2105       int dst_y = PANEL_YPOS(pos);
2106
2107       if (value == EL_UNDEFINED || value == EL_EMPTY)
2108       {
2109         int src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2110         int src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2111
2112         BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2113                    size, size, dst_x, dst_y);
2114       }
2115       else
2116       {
2117         int graphic = el2panelimg(value);
2118
2119         DrawSizedGraphicExt(drawto, dst_x, dst_y, graphic, frame, size);
2120       }
2121     }
2122     else if (type == TYPE_STRING)
2123     {
2124       boolean active = (value != 0);
2125       char *state_normal = "off";
2126       char *state_active = "on";
2127       char *state = (active ? state_active : state_normal);
2128       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2129                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2130                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2131                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2132
2133       if (nr == GAME_PANEL_GRAVITY_STATE)
2134       {
2135         int font1 = pos->font;          /* (used for normal state) */
2136         int font2 = pos->font_alt;      /* (used for active state) */
2137         int size1 = strlen(state_normal);
2138         int size2 = strlen(state_active);
2139         int width1 = size1 * getFontWidth(font1);
2140         int width2 = size2 * getFontWidth(font2);
2141         int max_width = MAX(width1, width2);
2142         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2143
2144         pos->width = max_width;
2145
2146         /* clear background for values that may have changed its size */
2147         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2148                                    max_width, max_height);
2149
2150         font = (active ? font2 : font1);
2151       }
2152
2153 #if 1
2154       /* as with numbers, don't truncate output if "chars" is zero or less */
2155       size = (size > 0 ? size : strlen(s));
2156 #endif
2157
2158       if (s != NULL)
2159       {
2160         char *s_cut = getStringCopyN(s, size);
2161
2162 #if 0
2163         /* (not needed anymore with above correction of "size" value) */
2164         size = strlen(s_cut);   /* string size may be smaller than "chars" */
2165 #endif
2166
2167         pos->width = size * getFontWidth(font);
2168
2169         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2170
2171         free(s_cut);
2172       }
2173     }
2174
2175     redraw_mask |= REDRAW_DOOR_1;
2176   }
2177
2178   game_status = GAME_MODE_PLAYING;
2179 }
2180
2181 void DrawGameValue_Emeralds(int value)
2182 {
2183   struct TextPosInfo *pos = &game.panel.gems;
2184 #if 1
2185   int font_nr = pos->font;
2186 #else
2187   int font_nr = FONT_TEXT_2;
2188 #endif
2189   int font_width = getFontWidth(font_nr);
2190   int chars = pos->size;
2191
2192 #if 1
2193   return;       /* !!! USE NEW STUFF !!! */
2194 #endif
2195
2196   if (PANEL_DEACTIVATED(pos))
2197     return;
2198
2199   pos->width = chars * font_width;
2200
2201   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2202 }
2203
2204 void DrawGameValue_Dynamite(int value)
2205 {
2206   struct TextPosInfo *pos = &game.panel.inventory_count;
2207 #if 1
2208   int font_nr = pos->font;
2209 #else
2210   int font_nr = FONT_TEXT_2;
2211 #endif
2212   int font_width = getFontWidth(font_nr);
2213   int chars = pos->size;
2214
2215 #if 1
2216   return;       /* !!! USE NEW STUFF !!! */
2217 #endif
2218
2219   if (PANEL_DEACTIVATED(pos))
2220     return;
2221
2222   pos->width = chars * font_width;
2223
2224   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2225 }
2226
2227 void DrawGameValue_Score(int value)
2228 {
2229   struct TextPosInfo *pos = &game.panel.score;
2230 #if 1
2231   int font_nr = pos->font;
2232 #else
2233   int font_nr = FONT_TEXT_2;
2234 #endif
2235   int font_width = getFontWidth(font_nr);
2236   int chars = pos->size;
2237
2238 #if 1
2239   return;       /* !!! USE NEW STUFF !!! */
2240 #endif
2241
2242   if (PANEL_DEACTIVATED(pos))
2243     return;
2244
2245   pos->width = chars * font_width;
2246
2247   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2248 }
2249
2250 void DrawGameValue_Time(int value)
2251 {
2252   struct TextPosInfo *pos = &game.panel.time;
2253   static int last_value = -1;
2254   int chars1 = 3;
2255   int chars2 = 4;
2256   int chars = pos->size;
2257 #if 1
2258   int font1_nr = pos->font;
2259   int font2_nr = pos->font_alt;
2260 #else
2261   int font1_nr = FONT_TEXT_2;
2262   int font2_nr = FONT_TEXT_1;
2263 #endif
2264   int font_nr = font1_nr;
2265   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2266
2267 #if 1
2268   return;       /* !!! USE NEW STUFF !!! */
2269 #endif
2270
2271   if (PANEL_DEACTIVATED(pos))
2272     return;
2273
2274   if (use_dynamic_chars)                /* use dynamic number of chars */
2275   {
2276     chars   = (value < 1000 ? chars1   : chars2);
2277     font_nr = (value < 1000 ? font1_nr : font2_nr);
2278   }
2279
2280   /* clear background if value just changed its size (dynamic chars only) */
2281   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2282   {
2283     int width1 = chars1 * getFontWidth(font1_nr);
2284     int width2 = chars2 * getFontWidth(font2_nr);
2285     int max_width = MAX(width1, width2);
2286     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2287
2288     pos->width = max_width;
2289
2290     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2291                                max_width, max_height);
2292   }
2293
2294   pos->width = chars * getFontWidth(font_nr);
2295
2296   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2297
2298   last_value = value;
2299 }
2300
2301 void DrawGameValue_Level(int value)
2302 {
2303   struct TextPosInfo *pos = &game.panel.level_number;
2304   int chars1 = 2;
2305   int chars2 = 3;
2306   int chars = pos->size;
2307 #if 1
2308   int font1_nr = pos->font;
2309   int font2_nr = pos->font_alt;
2310 #else
2311   int font1_nr = FONT_TEXT_2;
2312   int font2_nr = FONT_TEXT_1;
2313 #endif
2314   int font_nr = font1_nr;
2315   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2316
2317 #if 1
2318   return;       /* !!! USE NEW STUFF !!! */
2319 #endif
2320
2321   if (PANEL_DEACTIVATED(pos))
2322     return;
2323
2324   if (use_dynamic_chars)                /* use dynamic number of chars */
2325   {
2326     chars   = (level_nr < 100 ? chars1   : chars2);
2327     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2328   }
2329
2330   pos->width = chars * getFontWidth(font_nr);
2331
2332   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2333 }
2334
2335 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2336 {
2337 #if 0
2338   struct TextPosInfo *pos = &game.panel.keys;
2339 #endif
2340 #if 0
2341   int base_key_graphic = EL_KEY_1;
2342 #endif
2343   int i;
2344
2345 #if 1
2346   return;       /* !!! USE NEW STUFF !!! */
2347 #endif
2348
2349 #if 0
2350   if (PANEL_DEACTIVATED(pos))
2351     return;
2352 #endif
2353
2354 #if 0
2355   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2356     base_key_graphic = EL_EM_KEY_1;
2357 #endif
2358
2359 #if 0
2360   pos->width = 4 * MINI_TILEX;
2361 #endif
2362
2363 #if 1
2364   for (i = 0; i < MAX_NUM_KEYS; i++)
2365 #else
2366   /* currently only 4 of 8 possible keys are displayed */
2367   for (i = 0; i < STD_NUM_KEYS; i++)
2368 #endif
2369   {
2370 #if 1
2371     struct TextPosInfo *pos = &game.panel.key[i];
2372 #endif
2373     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2374     int src_y = DOOR_GFX_PAGEY1 + 123;
2375 #if 1
2376     int dst_x = PANEL_XPOS(pos);
2377     int dst_y = PANEL_YPOS(pos);
2378 #else
2379     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2380     int dst_y = PANEL_YPOS(pos);
2381 #endif
2382
2383 #if 1
2384     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2385                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2386                    EL_KEY_1) + i;
2387     int graphic = el2edimg(element);
2388 #endif
2389
2390 #if 1
2391     if (PANEL_DEACTIVATED(pos))
2392       continue;
2393 #endif
2394
2395 #if 0
2396     /* masked blit with tiles from half-size scaled bitmap does not work yet
2397        (no mask bitmap created for these sizes after loading and scaling) --
2398        solution: load without creating mask, scale, then create final mask */
2399
2400     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2401                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2402
2403     if (key[i])
2404     {
2405 #if 0
2406       int graphic = el2edimg(base_key_graphic + i);
2407 #endif
2408       Bitmap *src_bitmap;
2409       int src_x, src_y;
2410
2411       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2412
2413       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2414                     dst_x - src_x, dst_y - src_y);
2415       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2416                        dst_x, dst_y);
2417     }
2418 #else
2419 #if 1
2420     if (key[i])
2421       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2422     else
2423       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2424                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2425 #else
2426     if (key[i])
2427       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2428     else
2429       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2430                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2431 #endif
2432 #endif
2433   }
2434 }
2435
2436 #else
2437
2438 void DrawGameValue_Emeralds(int value)
2439 {
2440   int font_nr = FONT_TEXT_2;
2441   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2442
2443   if (PANEL_DEACTIVATED(game.panel.gems))
2444     return;
2445
2446   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2447 }
2448
2449 void DrawGameValue_Dynamite(int value)
2450 {
2451   int font_nr = FONT_TEXT_2;
2452   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2453
2454   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2455     return;
2456
2457   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2458 }
2459
2460 void DrawGameValue_Score(int value)
2461 {
2462   int font_nr = FONT_TEXT_2;
2463   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2464
2465   if (PANEL_DEACTIVATED(game.panel.score))
2466     return;
2467
2468   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2469 }
2470
2471 void DrawGameValue_Time(int value)
2472 {
2473   int font1_nr = FONT_TEXT_2;
2474 #if 1
2475   int font2_nr = FONT_TEXT_1;
2476 #else
2477   int font2_nr = FONT_LEVEL_NUMBER;
2478 #endif
2479   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2480   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2481
2482   if (PANEL_DEACTIVATED(game.panel.time))
2483     return;
2484
2485   /* clear background if value just changed its size */
2486   if (value == 999 || value == 1000)
2487     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2488
2489   if (value < 1000)
2490     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2491   else
2492     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2493 }
2494
2495 void DrawGameValue_Level(int value)
2496 {
2497   int font1_nr = FONT_TEXT_2;
2498 #if 1
2499   int font2_nr = FONT_TEXT_1;
2500 #else
2501   int font2_nr = FONT_LEVEL_NUMBER;
2502 #endif
2503
2504   if (PANEL_DEACTIVATED(game.panel.level))
2505     return;
2506
2507   if (level_nr < 100)
2508     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2509   else
2510     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2511 }
2512
2513 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2514 {
2515   int base_key_graphic = EL_KEY_1;
2516   int i;
2517
2518   if (PANEL_DEACTIVATED(game.panel.keys))
2519     return;
2520
2521   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2522     base_key_graphic = EL_EM_KEY_1;
2523
2524   /* currently only 4 of 8 possible keys are displayed */
2525   for (i = 0; i < STD_NUM_KEYS; i++)
2526   {
2527     int x = XX_KEYS + i * MINI_TILEX;
2528     int y = YY_KEYS;
2529
2530     if (key[i])
2531       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2532     else
2533       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2534                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2535   }
2536 }
2537
2538 #endif
2539
2540 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2541                        int key_bits)
2542 {
2543   int key[MAX_NUM_KEYS];
2544   int i;
2545
2546   /* prevent EM engine from updating time/score values parallel to GameWon() */
2547   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2548       local_player->LevelSolved)
2549     return;
2550
2551   for (i = 0; i < MAX_NUM_KEYS; i++)
2552     key[i] = key_bits & (1 << i);
2553
2554   DrawGameValue_Level(level_nr);
2555
2556   DrawGameValue_Emeralds(emeralds);
2557   DrawGameValue_Dynamite(dynamite);
2558   DrawGameValue_Score(score);
2559   DrawGameValue_Time(time);
2560
2561   DrawGameValue_Keys(key);
2562 }
2563
2564 void UpdateGameDoorValues()
2565 {
2566   UpdateGameControlValues();
2567 }
2568
2569 void DrawGameDoorValues()
2570 {
2571   DisplayGameControlValues();
2572 }
2573
2574 void DrawGameDoorValues_OLD()
2575 {
2576   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2577   int dynamite_value = 0;
2578   int score_value = (local_player->LevelSolved ? local_player->score_final :
2579                      local_player->score);
2580   int gems_value = local_player->gems_still_needed;
2581   int key_bits = 0;
2582   int i, j;
2583
2584   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2585   {
2586     DrawGameDoorValues_EM();
2587
2588     return;
2589   }
2590
2591   if (game.centered_player_nr == -1)
2592   {
2593     for (i = 0; i < MAX_PLAYERS; i++)
2594     {
2595       for (j = 0; j < MAX_NUM_KEYS; j++)
2596         if (stored_player[i].key[j])
2597           key_bits |= (1 << j);
2598
2599       dynamite_value += stored_player[i].inventory_size;
2600     }
2601   }
2602   else
2603   {
2604     int player_nr = game.centered_player_nr;
2605
2606     for (i = 0; i < MAX_NUM_KEYS; i++)
2607       if (stored_player[player_nr].key[i])
2608         key_bits |= (1 << i);
2609
2610     dynamite_value = stored_player[player_nr].inventory_size;
2611   }
2612
2613   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2614                     key_bits);
2615 }
2616
2617
2618 /*
2619   =============================================================================
2620   InitGameEngine()
2621   -----------------------------------------------------------------------------
2622   initialize game engine due to level / tape version number
2623   =============================================================================
2624 */
2625
2626 static void InitGameEngine()
2627 {
2628   int i, j, k, l, x, y;
2629
2630   /* set game engine from tape file when re-playing, else from level file */
2631   game.engine_version = (tape.playing ? tape.engine_version :
2632                          level.game_version);
2633
2634   /* ---------------------------------------------------------------------- */
2635   /* set flags for bugs and changes according to active game engine version */
2636   /* ---------------------------------------------------------------------- */
2637
2638   /*
2639     Summary of bugfix/change:
2640     Fixed handling for custom elements that change when pushed by the player.
2641
2642     Fixed/changed in version:
2643     3.1.0
2644
2645     Description:
2646     Before 3.1.0, custom elements that "change when pushing" changed directly
2647     after the player started pushing them (until then handled in "DigField()").
2648     Since 3.1.0, these custom elements are not changed until the "pushing"
2649     move of the element is finished (now handled in "ContinueMoving()").
2650
2651     Affected levels/tapes:
2652     The first condition is generally needed for all levels/tapes before version
2653     3.1.0, which might use the old behaviour before it was changed; known tapes
2654     that are affected are some tapes from the level set "Walpurgis Gardens" by
2655     Jamie Cullen.
2656     The second condition is an exception from the above case and is needed for
2657     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2658     above (including some development versions of 3.1.0), but before it was
2659     known that this change would break tapes like the above and was fixed in
2660     3.1.1, so that the changed behaviour was active although the engine version
2661     while recording maybe was before 3.1.0. There is at least one tape that is
2662     affected by this exception, which is the tape for the one-level set "Bug
2663     Machine" by Juergen Bonhagen.
2664   */
2665
2666   game.use_change_when_pushing_bug =
2667     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2668      !(tape.playing &&
2669        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2670        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2671
2672   /*
2673     Summary of bugfix/change:
2674     Fixed handling for blocking the field the player leaves when moving.
2675
2676     Fixed/changed in version:
2677     3.1.1
2678
2679     Description:
2680     Before 3.1.1, when "block last field when moving" was enabled, the field
2681     the player is leaving when moving was blocked for the time of the move,
2682     and was directly unblocked afterwards. This resulted in the last field
2683     being blocked for exactly one less than the number of frames of one player
2684     move. Additionally, even when blocking was disabled, the last field was
2685     blocked for exactly one frame.
2686     Since 3.1.1, due to changes in player movement handling, the last field
2687     is not blocked at all when blocking is disabled. When blocking is enabled,
2688     the last field is blocked for exactly the number of frames of one player
2689     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2690     last field is blocked for exactly one more than the number of frames of
2691     one player move.
2692
2693     Affected levels/tapes:
2694     (!!! yet to be determined -- probably many !!!)
2695   */
2696
2697   game.use_block_last_field_bug =
2698     (game.engine_version < VERSION_IDENT(3,1,1,0));
2699
2700   /*
2701     Summary of bugfix/change:
2702     Changed behaviour of CE changes with multiple changes per single frame.
2703
2704     Fixed/changed in version:
2705     3.2.0-6
2706
2707     Description:
2708     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2709     This resulted in race conditions where CEs seem to behave strange in some
2710     situations (where triggered CE changes were just skipped because there was
2711     already a CE change on that tile in the playfield in that engine frame).
2712     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2713     (The number of changes per frame must be limited in any case, because else
2714     it is easily possible to define CE changes that would result in an infinite
2715     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2716     should be set large enough so that it would only be reached in cases where
2717     the corresponding CE change conditions run into a loop. Therefore, it seems
2718     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2719     maximal number of change pages for custom elements.)
2720
2721     Affected levels/tapes:
2722     Probably many.
2723   */
2724
2725 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2726   game.max_num_changes_per_frame = 1;
2727 #else
2728   game.max_num_changes_per_frame =
2729     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2730 #endif
2731
2732   /* ---------------------------------------------------------------------- */
2733
2734   /* default scan direction: scan playfield from top/left to bottom/right */
2735   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2736
2737   /* dynamically adjust element properties according to game engine version */
2738   InitElementPropertiesEngine(game.engine_version);
2739
2740 #if 0
2741   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2742   printf("          tape version == %06d [%s] [file: %06d]\n",
2743          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2744          tape.file_version);
2745   printf("       => game.engine_version == %06d\n", game.engine_version);
2746 #endif
2747
2748   /* ---------- initialize player's initial move delay --------------------- */
2749
2750   /* dynamically adjust player properties according to level information */
2751   for (i = 0; i < MAX_PLAYERS; i++)
2752     game.initial_move_delay_value[i] =
2753       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2754
2755   /* dynamically adjust player properties according to game engine version */
2756   for (i = 0; i < MAX_PLAYERS; i++)
2757     game.initial_move_delay[i] =
2758       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2759        game.initial_move_delay_value[i] : 0);
2760
2761   /* ---------- initialize player's initial push delay --------------------- */
2762
2763   /* dynamically adjust player properties according to game engine version */
2764   game.initial_push_delay_value =
2765     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2766
2767   /* ---------- initialize changing elements ------------------------------- */
2768
2769   /* initialize changing elements information */
2770   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2771   {
2772     struct ElementInfo *ei = &element_info[i];
2773
2774     /* this pointer might have been changed in the level editor */
2775     ei->change = &ei->change_page[0];
2776
2777     if (!IS_CUSTOM_ELEMENT(i))
2778     {
2779       ei->change->target_element = EL_EMPTY_SPACE;
2780       ei->change->delay_fixed = 0;
2781       ei->change->delay_random = 0;
2782       ei->change->delay_frames = 1;
2783     }
2784
2785     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2786     {
2787       ei->has_change_event[j] = FALSE;
2788
2789       ei->event_page_nr[j] = 0;
2790       ei->event_page[j] = &ei->change_page[0];
2791     }
2792   }
2793
2794   /* add changing elements from pre-defined list */
2795   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2796   {
2797     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2798     struct ElementInfo *ei = &element_info[ch_delay->element];
2799
2800     ei->change->target_element       = ch_delay->target_element;
2801     ei->change->delay_fixed          = ch_delay->change_delay;
2802
2803     ei->change->pre_change_function  = ch_delay->pre_change_function;
2804     ei->change->change_function      = ch_delay->change_function;
2805     ei->change->post_change_function = ch_delay->post_change_function;
2806
2807     ei->change->can_change = TRUE;
2808     ei->change->can_change_or_has_action = TRUE;
2809
2810     ei->has_change_event[CE_DELAY] = TRUE;
2811
2812     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2813     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2814   }
2815
2816   /* ---------- initialize internal run-time variables ------------- */
2817
2818   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2819   {
2820     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2821
2822     for (j = 0; j < ei->num_change_pages; j++)
2823     {
2824       ei->change_page[j].can_change_or_has_action =
2825         (ei->change_page[j].can_change |
2826          ei->change_page[j].has_action);
2827     }
2828   }
2829
2830   /* add change events from custom element configuration */
2831   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2832   {
2833     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2834
2835     for (j = 0; j < ei->num_change_pages; j++)
2836     {
2837       if (!ei->change_page[j].can_change_or_has_action)
2838         continue;
2839
2840       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2841       {
2842         /* only add event page for the first page found with this event */
2843         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2844         {
2845           ei->has_change_event[k] = TRUE;
2846
2847           ei->event_page_nr[k] = j;
2848           ei->event_page[k] = &ei->change_page[j];
2849         }
2850       }
2851     }
2852   }
2853
2854   /* ---------- initialize run-time trigger player and element ------------- */
2855
2856   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2857   {
2858     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2859
2860     for (j = 0; j < ei->num_change_pages; j++)
2861     {
2862       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2863       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2864       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2865       ei->change_page[j].actual_trigger_ce_value = 0;
2866       ei->change_page[j].actual_trigger_ce_score = 0;
2867     }
2868   }
2869
2870   /* ---------- initialize trigger events ---------------------------------- */
2871
2872   /* initialize trigger events information */
2873   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2874     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2875       trigger_events[i][j] = FALSE;
2876
2877   /* add trigger events from element change event properties */
2878   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2879   {
2880     struct ElementInfo *ei = &element_info[i];
2881
2882     for (j = 0; j < ei->num_change_pages; j++)
2883     {
2884       if (!ei->change_page[j].can_change_or_has_action)
2885         continue;
2886
2887       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2888       {
2889         int trigger_element = ei->change_page[j].trigger_element;
2890
2891         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2892         {
2893           if (ei->change_page[j].has_event[k])
2894           {
2895             if (IS_GROUP_ELEMENT(trigger_element))
2896             {
2897               struct ElementGroupInfo *group =
2898                 element_info[trigger_element].group;
2899
2900               for (l = 0; l < group->num_elements_resolved; l++)
2901                 trigger_events[group->element_resolved[l]][k] = TRUE;
2902             }
2903             else if (trigger_element == EL_ANY_ELEMENT)
2904               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2905                 trigger_events[l][k] = TRUE;
2906             else
2907               trigger_events[trigger_element][k] = TRUE;
2908           }
2909         }
2910       }
2911     }
2912   }
2913
2914   /* ---------- initialize push delay -------------------------------------- */
2915
2916   /* initialize push delay values to default */
2917   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2918   {
2919     if (!IS_CUSTOM_ELEMENT(i))
2920     {
2921       /* set default push delay values (corrected since version 3.0.7-1) */
2922       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2923       {
2924         element_info[i].push_delay_fixed = 2;
2925         element_info[i].push_delay_random = 8;
2926       }
2927       else
2928       {
2929         element_info[i].push_delay_fixed = 8;
2930         element_info[i].push_delay_random = 8;
2931       }
2932     }
2933   }
2934
2935   /* set push delay value for certain elements from pre-defined list */
2936   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2937   {
2938     int e = push_delay_list[i].element;
2939
2940     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2941     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2942   }
2943
2944   /* set push delay value for Supaplex elements for newer engine versions */
2945   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2946   {
2947     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2948     {
2949       if (IS_SP_ELEMENT(i))
2950       {
2951         /* set SP push delay to just enough to push under a falling zonk */
2952         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2953
2954         element_info[i].push_delay_fixed  = delay;
2955         element_info[i].push_delay_random = 0;
2956       }
2957     }
2958   }
2959
2960   /* ---------- initialize move stepsize ----------------------------------- */
2961
2962   /* initialize move stepsize values to default */
2963   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2964     if (!IS_CUSTOM_ELEMENT(i))
2965       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2966
2967   /* set move stepsize value for certain elements from pre-defined list */
2968   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2969   {
2970     int e = move_stepsize_list[i].element;
2971
2972     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2973   }
2974
2975   /* ---------- initialize collect score ----------------------------------- */
2976
2977   /* initialize collect score values for custom elements from initial value */
2978   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2979     if (IS_CUSTOM_ELEMENT(i))
2980       element_info[i].collect_score = element_info[i].collect_score_initial;
2981
2982   /* ---------- initialize collect count ----------------------------------- */
2983
2984   /* initialize collect count values for non-custom elements */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].collect_count_initial = 0;
2988
2989   /* add collect count values for all elements from pre-defined list */
2990   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2991     element_info[collect_count_list[i].element].collect_count_initial =
2992       collect_count_list[i].count;
2993
2994   /* ---------- initialize access direction -------------------------------- */
2995
2996   /* initialize access direction values to default (access from every side) */
2997   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2998     if (!IS_CUSTOM_ELEMENT(i))
2999       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3000
3001   /* set access direction value for certain elements from pre-defined list */
3002   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3003     element_info[access_direction_list[i].element].access_direction =
3004       access_direction_list[i].direction;
3005
3006   /* ---------- initialize explosion content ------------------------------- */
3007   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3008   {
3009     if (IS_CUSTOM_ELEMENT(i))
3010       continue;
3011
3012     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3013     {
3014       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3015
3016       element_info[i].content.e[x][y] =
3017         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3018          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3019          i == EL_PLAYER_3 ? EL_EMERALD :
3020          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3021          i == EL_MOLE ? EL_EMERALD_RED :
3022          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3023          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3024          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3025          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3026          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3027          i == EL_WALL_EMERALD ? EL_EMERALD :
3028          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3029          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3030          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3031          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3032          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3033          i == EL_WALL_PEARL ? EL_PEARL :
3034          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3035          EL_EMPTY);
3036     }
3037   }
3038
3039   /* ---------- initialize recursion detection ------------------------------ */
3040   recursion_loop_depth = 0;
3041   recursion_loop_detected = FALSE;
3042   recursion_loop_element = EL_UNDEFINED;
3043
3044   /* ---------- initialize graphics engine ---------------------------------- */
3045   game.scroll_delay_value =
3046     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3047      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3048   game.scroll_delay_value =
3049     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3050 }
3051
3052 int get_num_special_action(int element, int action_first, int action_last)
3053 {
3054   int num_special_action = 0;
3055   int i, j;
3056
3057   for (i = action_first; i <= action_last; i++)
3058   {
3059     boolean found = FALSE;
3060
3061     for (j = 0; j < NUM_DIRECTIONS; j++)
3062       if (el_act_dir2img(element, i, j) !=
3063           el_act_dir2img(element, ACTION_DEFAULT, j))
3064         found = TRUE;
3065
3066     if (found)
3067       num_special_action++;
3068     else
3069       break;
3070   }
3071
3072   return num_special_action;
3073 }
3074
3075
3076 /*
3077   =============================================================================
3078   InitGame()
3079   -----------------------------------------------------------------------------
3080   initialize and start new game
3081   =============================================================================
3082 */
3083
3084 void InitGame()
3085 {
3086   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3087   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3088   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3089 #if 0
3090   boolean do_fading = (game_status == GAME_MODE_MAIN);
3091 #endif
3092   int i, j, x, y;
3093
3094   game_status = GAME_MODE_PLAYING;
3095
3096   InitGameEngine();
3097   InitGameControlValues();
3098
3099   /* don't play tapes over network */
3100   network_playing = (options.network && !tape.playing);
3101
3102   for (i = 0; i < MAX_PLAYERS; i++)
3103   {
3104     struct PlayerInfo *player = &stored_player[i];
3105
3106     player->index_nr = i;
3107     player->index_bit = (1 << i);
3108     player->element_nr = EL_PLAYER_1 + i;
3109
3110     player->present = FALSE;
3111     player->active = FALSE;
3112     player->killed = FALSE;
3113
3114     player->action = 0;
3115     player->effective_action = 0;
3116     player->programmed_action = 0;
3117
3118     player->score = 0;
3119     player->score_final = 0;
3120
3121     player->gems_still_needed = level.gems_needed;
3122     player->sokobanfields_still_needed = 0;
3123     player->lights_still_needed = 0;
3124     player->friends_still_needed = 0;
3125
3126     for (j = 0; j < MAX_NUM_KEYS; j++)
3127       player->key[j] = FALSE;
3128
3129     player->num_white_keys = 0;
3130
3131     player->dynabomb_count = 0;
3132     player->dynabomb_size = 1;
3133     player->dynabombs_left = 0;
3134     player->dynabomb_xl = FALSE;
3135
3136     player->MovDir = MV_NONE;
3137     player->MovPos = 0;
3138     player->GfxPos = 0;
3139     player->GfxDir = MV_NONE;
3140     player->GfxAction = ACTION_DEFAULT;
3141     player->Frame = 0;
3142     player->StepFrame = 0;
3143
3144     player->use_murphy = FALSE;
3145     player->artwork_element =
3146       (level.use_artwork_element[i] ? level.artwork_element[i] :
3147        player->element_nr);
3148
3149     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3150     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3151
3152     player->gravity = level.initial_player_gravity[i];
3153
3154     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3155
3156     player->actual_frame_counter = 0;
3157
3158     player->step_counter = 0;
3159
3160     player->last_move_dir = MV_NONE;
3161
3162     player->is_active = FALSE;
3163
3164     player->is_waiting = FALSE;
3165     player->is_moving = FALSE;
3166     player->is_auto_moving = FALSE;
3167     player->is_digging = FALSE;
3168     player->is_snapping = FALSE;
3169     player->is_collecting = FALSE;
3170     player->is_pushing = FALSE;
3171     player->is_switching = FALSE;
3172     player->is_dropping = FALSE;
3173     player->is_dropping_pressed = FALSE;
3174
3175     player->is_bored = FALSE;
3176     player->is_sleeping = FALSE;
3177
3178     player->frame_counter_bored = -1;
3179     player->frame_counter_sleeping = -1;
3180
3181     player->anim_delay_counter = 0;
3182     player->post_delay_counter = 0;
3183
3184     player->dir_waiting = MV_NONE;
3185     player->action_waiting = ACTION_DEFAULT;
3186     player->last_action_waiting = ACTION_DEFAULT;
3187     player->special_action_bored = ACTION_DEFAULT;
3188     player->special_action_sleeping = ACTION_DEFAULT;
3189
3190     player->switch_x = -1;
3191     player->switch_y = -1;
3192
3193     player->drop_x = -1;
3194     player->drop_y = -1;
3195
3196     player->show_envelope = 0;
3197
3198     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3199
3200     player->push_delay       = -1;      /* initialized when pushing starts */
3201     player->push_delay_value = game.initial_push_delay_value;
3202
3203     player->drop_delay = 0;
3204     player->drop_pressed_delay = 0;
3205
3206     player->last_jx = -1;
3207     player->last_jy = -1;
3208     player->jx = -1;
3209     player->jy = -1;
3210
3211     player->shield_normal_time_left = 0;
3212     player->shield_deadly_time_left = 0;
3213
3214     player->inventory_infinite_element = EL_UNDEFINED;
3215     player->inventory_size = 0;
3216
3217     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3218     SnapField(player, 0, 0);
3219
3220     player->LevelSolved = FALSE;
3221     player->GameOver = FALSE;
3222
3223     player->LevelSolved_GameWon = FALSE;
3224     player->LevelSolved_GameEnd = FALSE;
3225     player->LevelSolved_PanelOff = FALSE;
3226     player->LevelSolved_SaveTape = FALSE;
3227     player->LevelSolved_SaveScore = FALSE;
3228   }
3229
3230   network_player_action_received = FALSE;
3231
3232 #if defined(NETWORK_AVALIABLE)
3233   /* initial null action */
3234   if (network_playing)
3235     SendToServer_MovePlayer(MV_NONE);
3236 #endif
3237
3238   ZX = ZY = -1;
3239   ExitX = ExitY = -1;
3240
3241   FrameCounter = 0;
3242   TimeFrames = 0;
3243   TimePlayed = 0;
3244   TimeLeft = level.time;
3245   TapeTime = 0;
3246
3247   ScreenMovDir = MV_NONE;
3248   ScreenMovPos = 0;
3249   ScreenGfxPos = 0;
3250
3251   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3252
3253   AllPlayersGone = FALSE;
3254
3255   game.yamyam_content_nr = 0;
3256   game.robot_wheel_active = FALSE;
3257   game.magic_wall_active = FALSE;
3258   game.magic_wall_time_left = 0;
3259   game.light_time_left = 0;
3260   game.timegate_time_left = 0;
3261   game.switchgate_pos = 0;
3262   game.wind_direction = level.wind_direction_initial;
3263
3264 #if !USE_PLAYER_GRAVITY
3265   game.gravity = FALSE;
3266   game.explosions_delayed = TRUE;
3267 #endif
3268
3269   game.lenses_time_left = 0;
3270   game.magnify_time_left = 0;
3271
3272   game.ball_state = level.ball_state_initial;
3273   game.ball_content_nr = 0;
3274
3275   game.envelope_active = FALSE;
3276
3277   /* set focus to local player for network games, else to all players */
3278   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3279   game.centered_player_nr_next = game.centered_player_nr;
3280   game.set_centered_player = FALSE;
3281
3282   if (network_playing && tape.recording)
3283   {
3284     /* store client dependent player focus when recording network games */
3285     tape.centered_player_nr_next = game.centered_player_nr_next;
3286     tape.set_centered_player = TRUE;
3287   }
3288
3289   for (i = 0; i < NUM_BELTS; i++)
3290   {
3291     game.belt_dir[i] = MV_NONE;
3292     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3293   }
3294
3295   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3296     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3297
3298   SCAN_PLAYFIELD(x, y)
3299   {
3300     Feld[x][y] = level.field[x][y];
3301     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3302     ChangeDelay[x][y] = 0;
3303     ChangePage[x][y] = -1;
3304 #if USE_NEW_CUSTOM_VALUE
3305     CustomValue[x][y] = 0;              /* initialized in InitField() */
3306 #endif
3307     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3308     AmoebaNr[x][y] = 0;
3309     WasJustMoving[x][y] = 0;
3310     WasJustFalling[x][y] = 0;
3311     CheckCollision[x][y] = 0;
3312     CheckImpact[x][y] = 0;
3313     Stop[x][y] = FALSE;
3314     Pushed[x][y] = FALSE;
3315
3316     ChangeCount[x][y] = 0;
3317     ChangeEvent[x][y] = -1;
3318
3319     ExplodePhase[x][y] = 0;
3320     ExplodeDelay[x][y] = 0;
3321     ExplodeField[x][y] = EX_TYPE_NONE;
3322
3323     RunnerVisit[x][y] = 0;
3324     PlayerVisit[x][y] = 0;
3325
3326     GfxFrame[x][y] = 0;
3327     GfxRandom[x][y] = INIT_GFX_RANDOM();
3328     GfxElement[x][y] = EL_UNDEFINED;
3329     GfxAction[x][y] = ACTION_DEFAULT;
3330     GfxDir[x][y] = MV_NONE;
3331   }
3332
3333   SCAN_PLAYFIELD(x, y)
3334   {
3335     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3336       emulate_bd = FALSE;
3337     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3338       emulate_sb = FALSE;
3339     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3340       emulate_sp = FALSE;
3341
3342     InitField(x, y, TRUE);
3343   }
3344
3345   InitBeltMovement();
3346
3347   for (i = 0; i < MAX_PLAYERS; i++)
3348   {
3349     struct PlayerInfo *player = &stored_player[i];
3350
3351     /* set number of special actions for bored and sleeping animation */
3352     player->num_special_action_bored =
3353       get_num_special_action(player->artwork_element,
3354                              ACTION_BORING_1, ACTION_BORING_LAST);
3355     player->num_special_action_sleeping =
3356       get_num_special_action(player->artwork_element,
3357                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3358   }
3359
3360   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3361                     emulate_sb ? EMU_SOKOBAN :
3362                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3363
3364 #if USE_NEW_ALL_SLIPPERY
3365   /* initialize type of slippery elements */
3366   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3367   {
3368     if (!IS_CUSTOM_ELEMENT(i))
3369     {
3370       /* default: elements slip down either to the left or right randomly */
3371       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3372
3373       /* SP style elements prefer to slip down on the left side */
3374       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3375         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3376
3377       /* BD style elements prefer to slip down on the left side */
3378       if (game.emulation == EMU_BOULDERDASH)
3379         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3380     }
3381   }
3382 #endif
3383
3384   /* initialize explosion and ignition delay */
3385   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3386   {
3387     if (!IS_CUSTOM_ELEMENT(i))
3388     {
3389       int num_phase = 8;
3390       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3391                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3392                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3393       int last_phase = (num_phase + 1) * delay;
3394       int half_phase = (num_phase / 2) * delay;
3395
3396       element_info[i].explosion_delay = last_phase - 1;
3397       element_info[i].ignition_delay = half_phase;
3398
3399       if (i == EL_BLACK_ORB)
3400         element_info[i].ignition_delay = 1;
3401     }
3402
3403 #if 0
3404     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3405       element_info[i].explosion_delay = 1;
3406
3407     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3408       element_info[i].ignition_delay = 1;
3409 #endif
3410   }
3411
3412   /* correct non-moving belts to start moving left */
3413   for (i = 0; i < NUM_BELTS; i++)
3414     if (game.belt_dir[i] == MV_NONE)
3415       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3416
3417   /* check if any connected player was not found in playfield */
3418   for (i = 0; i < MAX_PLAYERS; i++)
3419   {
3420     struct PlayerInfo *player = &stored_player[i];
3421
3422     if (player->connected && !player->present)
3423     {
3424       for (j = 0; j < MAX_PLAYERS; j++)
3425       {
3426         struct PlayerInfo *some_player = &stored_player[j];
3427         int jx = some_player->jx, jy = some_player->jy;
3428
3429         /* assign first free player found that is present in the playfield */
3430         if (some_player->present && !some_player->connected)
3431         {
3432           player->present = TRUE;
3433           player->active = TRUE;
3434
3435           some_player->present = FALSE;
3436           some_player->active = FALSE;
3437
3438           player->artwork_element = some_player->artwork_element;
3439
3440           player->block_last_field       = some_player->block_last_field;
3441           player->block_delay_adjustment = some_player->block_delay_adjustment;
3442
3443           StorePlayer[jx][jy] = player->element_nr;
3444           player->jx = player->last_jx = jx;
3445           player->jy = player->last_jy = jy;
3446
3447           break;
3448         }
3449       }
3450     }
3451   }
3452
3453   if (tape.playing)
3454   {
3455     /* when playing a tape, eliminate all players who do not participate */
3456
3457     for (i = 0; i < MAX_PLAYERS; i++)
3458     {
3459       if (stored_player[i].active && !tape.player_participates[i])
3460       {
3461         struct PlayerInfo *player = &stored_player[i];
3462         int jx = player->jx, jy = player->jy;
3463
3464         player->active = FALSE;
3465         StorePlayer[jx][jy] = 0;
3466         Feld[jx][jy] = EL_EMPTY;
3467       }
3468     }
3469   }
3470   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3471   {
3472     /* when in single player mode, eliminate all but the first active player */
3473
3474     for (i = 0; i < MAX_PLAYERS; i++)
3475     {
3476       if (stored_player[i].active)
3477       {
3478         for (j = i + 1; j < MAX_PLAYERS; j++)
3479         {
3480           if (stored_player[j].active)
3481           {
3482             struct PlayerInfo *player = &stored_player[j];
3483             int jx = player->jx, jy = player->jy;
3484
3485             player->active = FALSE;
3486             player->present = FALSE;
3487
3488             StorePlayer[jx][jy] = 0;
3489             Feld[jx][jy] = EL_EMPTY;
3490           }
3491         }
3492       }
3493     }
3494   }
3495
3496   /* when recording the game, store which players take part in the game */
3497   if (tape.recording)
3498   {
3499     for (i = 0; i < MAX_PLAYERS; i++)
3500       if (stored_player[i].active)
3501         tape.player_participates[i] = TRUE;
3502   }
3503
3504   if (options.debug)
3505   {
3506     for (i = 0; i < MAX_PLAYERS; i++)
3507     {
3508       struct PlayerInfo *player = &stored_player[i];
3509
3510       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3511              i+1,
3512              player->present,
3513              player->connected,
3514              player->active);
3515       if (local_player == player)
3516         printf("Player  %d is local player.\n", i+1);
3517     }
3518   }
3519
3520   if (BorderElement == EL_EMPTY)
3521   {
3522     SBX_Left = 0;
3523     SBX_Right = lev_fieldx - SCR_FIELDX;
3524     SBY_Upper = 0;
3525     SBY_Lower = lev_fieldy - SCR_FIELDY;
3526   }
3527   else
3528   {
3529     SBX_Left = -1;
3530     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3531     SBY_Upper = -1;
3532     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3533   }
3534
3535   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3536     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3537
3538   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3539     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3540
3541   /* if local player not found, look for custom element that might create
3542      the player (make some assumptions about the right custom element) */
3543   if (!local_player->present)
3544   {
3545     int start_x = 0, start_y = 0;
3546     int found_rating = 0;
3547     int found_element = EL_UNDEFINED;
3548     int player_nr = local_player->index_nr;
3549
3550     SCAN_PLAYFIELD(x, y)
3551     {
3552       int element = Feld[x][y];
3553       int content;
3554       int xx, yy;
3555       boolean is_player;
3556
3557       if (level.use_start_element[player_nr] &&
3558           level.start_element[player_nr] == element &&
3559           found_rating < 4)
3560       {
3561         start_x = x;
3562         start_y = y;
3563
3564         found_rating = 4;
3565         found_element = element;
3566       }
3567
3568       if (!IS_CUSTOM_ELEMENT(element))
3569         continue;
3570
3571       if (CAN_CHANGE(element))
3572       {
3573         for (i = 0; i < element_info[element].num_change_pages; i++)
3574         {
3575           /* check for player created from custom element as single target */
3576           content = element_info[element].change_page[i].target_element;
3577           is_player = ELEM_IS_PLAYER(content);
3578
3579           if (is_player && (found_rating < 3 ||
3580                             (found_rating == 3 && element < found_element)))
3581           {
3582             start_x = x;
3583             start_y = y;
3584
3585             found_rating = 3;
3586             found_element = element;
3587           }
3588         }
3589       }
3590
3591       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3592       {
3593         /* check for player created from custom element as explosion content */
3594         content = element_info[element].content.e[xx][yy];
3595         is_player = ELEM_IS_PLAYER(content);
3596
3597         if (is_player && (found_rating < 2 ||
3598                           (found_rating == 2 && element < found_element)))
3599         {
3600           start_x = x + xx - 1;
3601           start_y = y + yy - 1;
3602
3603           found_rating = 2;
3604           found_element = element;
3605         }
3606
3607         if (!CAN_CHANGE(element))
3608           continue;
3609
3610         for (i = 0; i < element_info[element].num_change_pages; i++)
3611         {
3612           /* check for player created from custom element as extended target */
3613           content =
3614             element_info[element].change_page[i].target_content.e[xx][yy];
3615
3616           is_player = ELEM_IS_PLAYER(content);
3617
3618           if (is_player && (found_rating < 1 ||
3619                             (found_rating == 1 && element < found_element)))
3620           {
3621             start_x = x + xx - 1;
3622             start_y = y + yy - 1;
3623
3624             found_rating = 1;
3625             found_element = element;
3626           }
3627         }
3628       }
3629     }
3630
3631     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3632                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3633                 start_x - MIDPOSX);
3634
3635     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3636                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3637                 start_y - MIDPOSY);
3638   }
3639   else
3640   {
3641     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3642                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3643                 local_player->jx - MIDPOSX);
3644
3645     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3646                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3647                 local_player->jy - MIDPOSY);
3648   }
3649
3650   /* do not use PLAYING mask for fading out from main screen */
3651   game_status = GAME_MODE_MAIN;
3652
3653   StopAnimation();
3654
3655   if (!game.restart_level)
3656     CloseDoor(DOOR_CLOSE_1);
3657
3658 #if 1
3659   if (level_editor_test_game)
3660     FadeSkipNextFadeIn();
3661   else
3662     FadeSetEnterScreen();
3663 #else
3664   if (level_editor_test_game)
3665     fading = fading_none;
3666   else
3667     fading = menu.destination;
3668 #endif
3669
3670 #if 1
3671   FadeOut(REDRAW_FIELD);
3672 #else
3673   if (do_fading)
3674     FadeOut(REDRAW_FIELD);
3675 #endif
3676
3677   game_status = GAME_MODE_PLAYING;
3678
3679   /* !!! FIX THIS (START) !!! */
3680   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3681   {
3682     InitGameEngine_EM();
3683
3684     /* blit playfield from scroll buffer to normal back buffer for fading in */
3685     BlitScreenToBitmap_EM(backbuffer);
3686   }
3687   else
3688   {
3689     DrawLevel();
3690     DrawAllPlayers();
3691
3692     /* after drawing the level, correct some elements */
3693     if (game.timegate_time_left == 0)
3694       CloseAllOpenTimegates();
3695
3696     /* blit playfield from scroll buffer to normal back buffer for fading in */
3697     if (setup.soft_scrolling)
3698       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3699
3700     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3701   }
3702   /* !!! FIX THIS (END) !!! */
3703
3704 #if 1
3705   FadeIn(REDRAW_FIELD);
3706 #else
3707   if (do_fading)
3708     FadeIn(REDRAW_FIELD);
3709
3710   BackToFront();
3711 #endif
3712
3713   if (!game.restart_level)
3714   {
3715     /* copy default game door content to main double buffer */
3716     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3717                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3718   }
3719
3720   SetPanelBackground();
3721   SetDrawBackgroundMask(REDRAW_DOOR_1);
3722
3723   UpdateGameDoorValues();
3724   DrawGameDoorValues();
3725
3726   if (!game.restart_level)
3727   {
3728     UnmapGameButtons();
3729     UnmapTapeButtons();
3730     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3731     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3732     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3733     MapGameButtons();
3734     MapTapeButtons();
3735
3736     /* copy actual game door content to door double buffer for OpenDoor() */
3737     BlitBitmap(drawto, bitmap_db_door,
3738                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3739
3740     OpenDoor(DOOR_OPEN_ALL);
3741
3742     PlaySound(SND_GAME_STARTING);
3743
3744     if (setup.sound_music)
3745       PlayLevelMusic();
3746
3747     KeyboardAutoRepeatOffUnlessAutoplay();
3748
3749     if (options.debug)
3750     {
3751       for (i = 0; i < MAX_PLAYERS; i++)
3752         printf("Player %d %sactive.\n",
3753                i + 1, (stored_player[i].active ? "" : "not "));
3754     }
3755   }
3756
3757 #if 1
3758   UnmapAllGadgets();
3759
3760   MapGameButtons();
3761   MapTapeButtons();
3762 #endif
3763
3764   game.restart_level = FALSE;
3765 }
3766
3767 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3768 {
3769   /* this is used for non-R'n'D game engines to update certain engine values */
3770
3771   /* needed to determine if sounds are played within the visible screen area */
3772   scroll_x = actual_scroll_x;
3773   scroll_y = actual_scroll_y;
3774 }
3775
3776 void InitMovDir(int x, int y)
3777 {
3778   int i, element = Feld[x][y];
3779   static int xy[4][2] =
3780   {
3781     {  0, +1 },
3782     { +1,  0 },
3783     {  0, -1 },
3784     { -1,  0 }
3785   };
3786   static int direction[3][4] =
3787   {
3788     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3789     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3790     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3791   };
3792
3793   switch (element)
3794   {
3795     case EL_BUG_RIGHT:
3796     case EL_BUG_UP:
3797     case EL_BUG_LEFT:
3798     case EL_BUG_DOWN:
3799       Feld[x][y] = EL_BUG;
3800       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3801       break;
3802
3803     case EL_SPACESHIP_RIGHT:
3804     case EL_SPACESHIP_UP:
3805     case EL_SPACESHIP_LEFT:
3806     case EL_SPACESHIP_DOWN:
3807       Feld[x][y] = EL_SPACESHIP;
3808       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3809       break;
3810
3811     case EL_BD_BUTTERFLY_RIGHT:
3812     case EL_BD_BUTTERFLY_UP:
3813     case EL_BD_BUTTERFLY_LEFT:
3814     case EL_BD_BUTTERFLY_DOWN:
3815       Feld[x][y] = EL_BD_BUTTERFLY;
3816       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3817       break;
3818
3819     case EL_BD_FIREFLY_RIGHT:
3820     case EL_BD_FIREFLY_UP:
3821     case EL_BD_FIREFLY_LEFT:
3822     case EL_BD_FIREFLY_DOWN:
3823       Feld[x][y] = EL_BD_FIREFLY;
3824       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3825       break;
3826
3827     case EL_PACMAN_RIGHT:
3828     case EL_PACMAN_UP:
3829     case EL_PACMAN_LEFT:
3830     case EL_PACMAN_DOWN:
3831       Feld[x][y] = EL_PACMAN;
3832       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3833       break;
3834
3835     case EL_YAMYAM_LEFT:
3836     case EL_YAMYAM_RIGHT:
3837     case EL_YAMYAM_UP:
3838     case EL_YAMYAM_DOWN:
3839       Feld[x][y] = EL_YAMYAM;
3840       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3841       break;
3842
3843     case EL_SP_SNIKSNAK:
3844       MovDir[x][y] = MV_UP;
3845       break;
3846
3847     case EL_SP_ELECTRON:
3848       MovDir[x][y] = MV_LEFT;
3849       break;
3850
3851     case EL_MOLE_LEFT:
3852     case EL_MOLE_RIGHT:
3853     case EL_MOLE_UP:
3854     case EL_MOLE_DOWN:
3855       Feld[x][y] = EL_MOLE;
3856       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3857       break;
3858
3859     default:
3860       if (IS_CUSTOM_ELEMENT(element))
3861       {
3862         struct ElementInfo *ei = &element_info[element];
3863         int move_direction_initial = ei->move_direction_initial;
3864         int move_pattern = ei->move_pattern;
3865
3866         if (move_direction_initial == MV_START_PREVIOUS)
3867         {
3868           if (MovDir[x][y] != MV_NONE)
3869             return;
3870
3871           move_direction_initial = MV_START_AUTOMATIC;
3872         }
3873
3874         if (move_direction_initial == MV_START_RANDOM)
3875           MovDir[x][y] = 1 << RND(4);
3876         else if (move_direction_initial & MV_ANY_DIRECTION)
3877           MovDir[x][y] = move_direction_initial;
3878         else if (move_pattern == MV_ALL_DIRECTIONS ||
3879                  move_pattern == MV_TURNING_LEFT ||
3880                  move_pattern == MV_TURNING_RIGHT ||
3881                  move_pattern == MV_TURNING_LEFT_RIGHT ||
3882                  move_pattern == MV_TURNING_RIGHT_LEFT ||
3883                  move_pattern == MV_TURNING_RANDOM)
3884           MovDir[x][y] = 1 << RND(4);
3885         else if (move_pattern == MV_HORIZONTAL)
3886           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
3887         else if (move_pattern == MV_VERTICAL)
3888           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
3889         else if (move_pattern & MV_ANY_DIRECTION)
3890           MovDir[x][y] = element_info[element].move_pattern;
3891         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
3892                  move_pattern == MV_ALONG_RIGHT_SIDE)
3893         {
3894           /* use random direction as default start direction */
3895           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3896             MovDir[x][y] = 1 << RND(4);
3897
3898           for (i = 0; i < NUM_DIRECTIONS; i++)
3899           {
3900             int x1 = x + xy[i][0];
3901             int y1 = y + xy[i][1];
3902
3903             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3904             {
3905               if (move_pattern == MV_ALONG_RIGHT_SIDE)
3906                 MovDir[x][y] = direction[0][i];
3907               else
3908                 MovDir[x][y] = direction[1][i];
3909
3910               break;
3911             }
3912           }
3913         }                
3914       }
3915       else
3916       {
3917         MovDir[x][y] = 1 << RND(4);
3918
3919         if (element != EL_BUG &&
3920             element != EL_SPACESHIP &&
3921             element != EL_BD_BUTTERFLY &&
3922             element != EL_BD_FIREFLY)
3923           break;
3924
3925         for (i = 0; i < NUM_DIRECTIONS; i++)
3926         {
3927           int x1 = x + xy[i][0];
3928           int y1 = y + xy[i][1];
3929
3930           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
3931           {
3932             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
3933             {
3934               MovDir[x][y] = direction[0][i];
3935               break;
3936             }
3937             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
3938                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
3939             {
3940               MovDir[x][y] = direction[1][i];
3941               break;
3942             }
3943           }
3944         }
3945       }
3946       break;
3947   }
3948
3949   GfxDir[x][y] = MovDir[x][y];
3950 }
3951
3952 void InitAmoebaNr(int x, int y)
3953 {
3954   int i;
3955   int group_nr = AmoebeNachbarNr(x, y);
3956
3957   if (group_nr == 0)
3958   {
3959     for (i = 1; i < MAX_NUM_AMOEBA; i++)
3960     {
3961       if (AmoebaCnt[i] == 0)
3962       {
3963         group_nr = i;
3964         break;
3965       }
3966     }
3967   }
3968
3969   AmoebaNr[x][y] = group_nr;
3970   AmoebaCnt[group_nr]++;
3971   AmoebaCnt2[group_nr]++;
3972 }
3973
3974 static void PlayerWins(struct PlayerInfo *player)
3975 {
3976   player->LevelSolved = TRUE;
3977   player->GameOver = TRUE;
3978
3979   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
3980                          level.native_em_level->lev->score : player->score);
3981 }
3982
3983 void GameWon()
3984 {
3985   static int time, time_final;
3986   static int score, score_final;
3987   static int game_over_delay_1 = 0;
3988   static int game_over_delay_2 = 0;
3989   int game_over_delay_value_1 = 50;
3990   int game_over_delay_value_2 = 50;
3991
3992   if (!local_player->LevelSolved_GameWon)
3993   {
3994     int i;
3995
3996     /* do not start end game actions before the player stops moving (to exit) */
3997     if (local_player->MovPos)
3998       return;
3999
4000     local_player->LevelSolved_GameWon = TRUE;
4001     local_player->LevelSolved_SaveTape = tape.recording;
4002     local_player->LevelSolved_SaveScore = !tape.playing;
4003
4004     if (tape.auto_play)         /* tape might already be stopped here */
4005       tape.auto_play_level_solved = TRUE;
4006
4007 #if 1
4008     TapeStop();
4009 #endif
4010
4011     game_over_delay_1 = game_over_delay_value_1;
4012     game_over_delay_2 = game_over_delay_value_2;
4013
4014     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4015     score = score_final = local_player->score_final;
4016
4017     if (TimeLeft > 0)
4018     {
4019       time_final = 0;
4020       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4021     }
4022     else if (level.time == 0 && TimePlayed < 999)
4023     {
4024       time_final = 999;
4025       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4026     }
4027
4028     local_player->score_final = score_final;
4029
4030     if (level_editor_test_game)
4031     {
4032       time = time_final;
4033       score = score_final;
4034
4035 #if 1
4036       game_panel_controls[GAME_PANEL_TIME].value = time;
4037       game_panel_controls[GAME_PANEL_SCORE].value = score;
4038
4039       DisplayGameControlValues();
4040 #else
4041       DrawGameValue_Time(time);
4042       DrawGameValue_Score(score);
4043 #endif
4044     }
4045
4046     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4047     {
4048       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4049       {
4050         /* close exit door after last player */
4051         if ((AllPlayersGone &&
4052              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4053               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4054               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4055             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4056             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4057         {
4058           int element = Feld[ExitX][ExitY];
4059
4060 #if 0
4061           if (element == EL_EM_EXIT_OPEN ||
4062               element == EL_EM_STEEL_EXIT_OPEN)
4063           {
4064             Bang(ExitX, ExitY);
4065           }
4066           else
4067 #endif
4068           {
4069             Feld[ExitX][ExitY] =
4070               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4071                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4072                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4073                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4074                EL_EM_STEEL_EXIT_CLOSING);
4075
4076             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4077           }
4078         }
4079
4080         /* player disappears */
4081         DrawLevelField(ExitX, ExitY);
4082       }
4083
4084       for (i = 0; i < MAX_PLAYERS; i++)
4085       {
4086         struct PlayerInfo *player = &stored_player[i];
4087
4088         if (player->present)
4089         {
4090           RemovePlayer(player);
4091
4092           /* player disappears */
4093           DrawLevelField(player->jx, player->jy);
4094         }
4095       }
4096     }
4097
4098     PlaySound(SND_GAME_WINNING);
4099   }
4100
4101   if (game_over_delay_1 > 0)
4102   {
4103     game_over_delay_1--;
4104
4105     return;
4106   }
4107
4108   if (time != time_final)
4109   {
4110     int time_to_go = ABS(time_final - time);
4111     int time_count_dir = (time < time_final ? +1 : -1);
4112     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4113
4114     time  += time_count_steps * time_count_dir;
4115     score += time_count_steps * level.score[SC_TIME_BONUS];
4116
4117 #if 1
4118     game_panel_controls[GAME_PANEL_TIME].value = time;
4119     game_panel_controls[GAME_PANEL_SCORE].value = score;
4120
4121     DisplayGameControlValues();
4122 #else
4123     DrawGameValue_Time(time);
4124     DrawGameValue_Score(score);
4125 #endif
4126
4127     if (time == time_final)
4128       StopSound(SND_GAME_LEVELTIME_BONUS);
4129     else if (setup.sound_loops)
4130       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4131     else
4132       PlaySound(SND_GAME_LEVELTIME_BONUS);
4133
4134     return;
4135   }
4136
4137   local_player->LevelSolved_PanelOff = TRUE;
4138
4139   if (game_over_delay_2 > 0)
4140   {
4141     game_over_delay_2--;
4142
4143     return;
4144   }
4145
4146 #if 1
4147   GameEnd();
4148 #endif
4149 }
4150
4151 void GameEnd()
4152 {
4153   int hi_pos;
4154   boolean raise_level = FALSE;
4155
4156   local_player->LevelSolved_GameEnd = TRUE;
4157
4158   CloseDoor(DOOR_CLOSE_1);
4159
4160   if (local_player->LevelSolved_SaveTape)
4161   {
4162 #if 0
4163     TapeStop();
4164 #endif
4165
4166 #if 1
4167     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4168 #else
4169     SaveTape(tape.level_nr);            /* ask to save tape */
4170 #endif
4171   }
4172
4173   if (level_editor_test_game)
4174   {
4175     game_status = GAME_MODE_MAIN;
4176
4177 #if 1
4178     DrawAndFadeInMainMenu(REDRAW_FIELD);
4179 #else
4180     DrawMainMenu();
4181 #endif
4182
4183     return;
4184   }
4185
4186   if (!local_player->LevelSolved_SaveScore)
4187   {
4188 #if 1
4189     FadeOut(REDRAW_FIELD);
4190 #endif
4191
4192     game_status = GAME_MODE_MAIN;
4193
4194     DrawAndFadeInMainMenu(REDRAW_FIELD);
4195
4196     return;
4197   }
4198
4199   if (level_nr == leveldir_current->handicap_level)
4200   {
4201     leveldir_current->handicap_level++;
4202     SaveLevelSetup_SeriesInfo();
4203   }
4204
4205   if (level_nr < leveldir_current->last_level)
4206     raise_level = TRUE;                 /* advance to next level */
4207
4208   if ((hi_pos = NewHiScore()) >= 0) 
4209   {
4210     game_status = GAME_MODE_SCORES;
4211
4212     DrawHallOfFame(hi_pos);
4213
4214     if (raise_level)
4215     {
4216       level_nr++;
4217       TapeErase();
4218     }
4219   }
4220   else
4221   {
4222 #if 1
4223     FadeOut(REDRAW_FIELD);
4224 #endif
4225
4226     game_status = GAME_MODE_MAIN;
4227
4228     if (raise_level)
4229     {
4230       level_nr++;
4231       TapeErase();
4232     }
4233
4234     DrawAndFadeInMainMenu(REDRAW_FIELD);
4235   }
4236 }
4237
4238 int NewHiScore()
4239 {
4240   int k, l;
4241   int position = -1;
4242
4243   LoadScore(level_nr);
4244
4245   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4246       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4247     return -1;
4248
4249   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4250   {
4251     if (local_player->score_final > highscore[k].Score)
4252     {
4253       /* player has made it to the hall of fame */
4254
4255       if (k < MAX_SCORE_ENTRIES - 1)
4256       {
4257         int m = MAX_SCORE_ENTRIES - 1;
4258
4259 #ifdef ONE_PER_NAME
4260         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4261           if (strEqual(setup.player_name, highscore[l].Name))
4262             m = l;
4263         if (m == k)     /* player's new highscore overwrites his old one */
4264           goto put_into_list;
4265 #endif
4266
4267         for (l = m; l > k; l--)
4268         {
4269           strcpy(highscore[l].Name, highscore[l - 1].Name);
4270           highscore[l].Score = highscore[l - 1].Score;
4271         }
4272       }
4273
4274 #ifdef ONE_PER_NAME
4275       put_into_list:
4276 #endif
4277       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4278       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4279       highscore[k].Score = local_player->score_final; 
4280       position = k;
4281       break;
4282     }
4283
4284 #ifdef ONE_PER_NAME
4285     else if (!strncmp(setup.player_name, highscore[k].Name,
4286                       MAX_PLAYER_NAME_LEN))
4287       break;    /* player already there with a higher score */
4288 #endif
4289
4290   }
4291
4292   if (position >= 0) 
4293     SaveScore(level_nr);
4294
4295   return position;
4296 }
4297
4298 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4299 {
4300   int element = Feld[x][y];
4301   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4302   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4303   int horiz_move = (dx != 0);
4304   int sign = (horiz_move ? dx : dy);
4305   int step = sign * element_info[element].move_stepsize;
4306
4307   /* special values for move stepsize for spring and things on conveyor belt */
4308   if (horiz_move)
4309   {
4310     if (CAN_FALL(element) &&
4311         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4312       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4313     else if (element == EL_SPRING)
4314       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4315   }
4316
4317   return step;
4318 }
4319
4320 inline static int getElementMoveStepsize(int x, int y)
4321 {
4322   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4323 }
4324
4325 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4326 {
4327   if (player->GfxAction != action || player->GfxDir != dir)
4328   {
4329 #if 0
4330     printf("Player frame reset! (%d => %d, %d => %d)\n",
4331            player->GfxAction, action, player->GfxDir, dir);
4332 #endif
4333
4334     player->GfxAction = action;
4335     player->GfxDir = dir;
4336     player->Frame = 0;
4337     player->StepFrame = 0;
4338   }
4339 }
4340
4341 #if USE_GFX_RESET_GFX_ANIMATION
4342 static void ResetGfxFrame(int x, int y, boolean redraw)
4343 {
4344   int element = Feld[x][y];
4345   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4346   int last_gfx_frame = GfxFrame[x][y];
4347
4348   if (graphic_info[graphic].anim_global_sync)
4349     GfxFrame[x][y] = FrameCounter;
4350   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4351     GfxFrame[x][y] = CustomValue[x][y];
4352   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4353     GfxFrame[x][y] = element_info[element].collect_score;
4354   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4355     GfxFrame[x][y] = ChangeDelay[x][y];
4356
4357   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4358     DrawLevelGraphicAnimation(x, y, graphic);
4359 }
4360 #endif
4361
4362 static void ResetGfxAnimation(int x, int y)
4363 {
4364   GfxAction[x][y] = ACTION_DEFAULT;
4365   GfxDir[x][y] = MovDir[x][y];
4366   GfxFrame[x][y] = 0;
4367
4368 #if USE_GFX_RESET_GFX_ANIMATION
4369   ResetGfxFrame(x, y, FALSE);
4370 #endif
4371 }
4372
4373 static void ResetRandomAnimationValue(int x, int y)
4374 {
4375   GfxRandom[x][y] = INIT_GFX_RANDOM();
4376 }
4377
4378 void InitMovingField(int x, int y, int direction)
4379 {
4380   int element = Feld[x][y];
4381   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4382   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4383   int newx = x + dx;
4384   int newy = y + dy;
4385   boolean is_moving_before, is_moving_after;
4386 #if 0
4387   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4388 #endif
4389
4390   /* check if element was/is moving or being moved before/after mode change */
4391 #if 1
4392 #if 1
4393   is_moving_before = (WasJustMoving[x][y] != 0);
4394 #else
4395   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4396   is_moving_before = WasJustMoving[x][y];
4397 #endif
4398 #else
4399   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4400 #endif
4401   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4402
4403   /* reset animation only for moving elements which change direction of moving
4404      or which just started or stopped moving
4405      (else CEs with property "can move" / "not moving" are reset each frame) */
4406 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4407 #if 1
4408   if (is_moving_before != is_moving_after ||
4409       direction != MovDir[x][y])
4410     ResetGfxAnimation(x, y);
4411 #else
4412   if ((is_moving_before || is_moving_after) && !continues_moving)
4413     ResetGfxAnimation(x, y);
4414 #endif
4415 #else
4416   if (!continues_moving)
4417     ResetGfxAnimation(x, y);
4418 #endif
4419
4420   MovDir[x][y] = direction;
4421   GfxDir[x][y] = direction;
4422
4423 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4424   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4425                      direction == MV_DOWN && CAN_FALL(element) ?
4426                      ACTION_FALLING : ACTION_MOVING);
4427 #else
4428   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4429                      ACTION_FALLING : ACTION_MOVING);
4430 #endif
4431
4432   /* this is needed for CEs with property "can move" / "not moving" */
4433
4434   if (is_moving_after)
4435   {
4436     if (Feld[newx][newy] == EL_EMPTY)
4437       Feld[newx][newy] = EL_BLOCKED;
4438
4439     MovDir[newx][newy] = MovDir[x][y];
4440
4441 #if USE_NEW_CUSTOM_VALUE
4442     CustomValue[newx][newy] = CustomValue[x][y];
4443 #endif
4444
4445     GfxFrame[newx][newy] = GfxFrame[x][y];
4446     GfxRandom[newx][newy] = GfxRandom[x][y];
4447     GfxAction[newx][newy] = GfxAction[x][y];
4448     GfxDir[newx][newy] = GfxDir[x][y];
4449   }
4450 }
4451
4452 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4453 {
4454   int direction = MovDir[x][y];
4455   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4456   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4457
4458   *goes_to_x = newx;
4459   *goes_to_y = newy;
4460 }
4461
4462 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4463 {
4464   int oldx = x, oldy = y;
4465   int direction = MovDir[x][y];
4466
4467   if (direction == MV_LEFT)
4468     oldx++;
4469   else if (direction == MV_RIGHT)
4470     oldx--;
4471   else if (direction == MV_UP)
4472     oldy++;
4473   else if (direction == MV_DOWN)
4474     oldy--;
4475
4476   *comes_from_x = oldx;
4477   *comes_from_y = oldy;
4478 }
4479
4480 int MovingOrBlocked2Element(int x, int y)
4481 {
4482   int element = Feld[x][y];
4483
4484   if (element == EL_BLOCKED)
4485   {
4486     int oldx, oldy;
4487
4488     Blocked2Moving(x, y, &oldx, &oldy);
4489     return Feld[oldx][oldy];
4490   }
4491   else
4492     return element;
4493 }
4494
4495 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4496 {
4497   /* like MovingOrBlocked2Element(), but if element is moving
4498      and (x,y) is the field the moving element is just leaving,
4499      return EL_BLOCKED instead of the element value */
4500   int element = Feld[x][y];
4501
4502   if (IS_MOVING(x, y))
4503   {
4504     if (element == EL_BLOCKED)
4505     {
4506       int oldx, oldy;
4507
4508       Blocked2Moving(x, y, &oldx, &oldy);
4509       return Feld[oldx][oldy];
4510     }
4511     else
4512       return EL_BLOCKED;
4513   }
4514   else
4515     return element;
4516 }
4517
4518 static void RemoveField(int x, int y)
4519 {
4520   Feld[x][y] = EL_EMPTY;
4521
4522   MovPos[x][y] = 0;
4523   MovDir[x][y] = 0;
4524   MovDelay[x][y] = 0;
4525
4526 #if USE_NEW_CUSTOM_VALUE
4527   CustomValue[x][y] = 0;
4528 #endif
4529
4530   AmoebaNr[x][y] = 0;
4531   ChangeDelay[x][y] = 0;
4532   ChangePage[x][y] = -1;
4533   Pushed[x][y] = FALSE;
4534
4535 #if 0
4536   ExplodeField[x][y] = EX_TYPE_NONE;
4537 #endif
4538
4539   GfxElement[x][y] = EL_UNDEFINED;
4540   GfxAction[x][y] = ACTION_DEFAULT;
4541   GfxDir[x][y] = MV_NONE;
4542 }
4543
4544 void RemoveMovingField(int x, int y)
4545 {
4546   int oldx = x, oldy = y, newx = x, newy = y;
4547   int element = Feld[x][y];
4548   int next_element = EL_UNDEFINED;
4549
4550   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4551     return;
4552
4553   if (IS_MOVING(x, y))
4554   {
4555     Moving2Blocked(x, y, &newx, &newy);
4556
4557     if (Feld[newx][newy] != EL_BLOCKED)
4558     {
4559       /* element is moving, but target field is not free (blocked), but
4560          already occupied by something different (example: acid pool);
4561          in this case, only remove the moving field, but not the target */
4562
4563       RemoveField(oldx, oldy);
4564
4565       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4566
4567       DrawLevelField(oldx, oldy);
4568
4569       return;
4570     }
4571   }
4572   else if (element == EL_BLOCKED)
4573   {
4574     Blocked2Moving(x, y, &oldx, &oldy);
4575     if (!IS_MOVING(oldx, oldy))
4576       return;
4577   }
4578
4579   if (element == EL_BLOCKED &&
4580       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4581        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4582        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4583        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4584        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4585        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4586     next_element = get_next_element(Feld[oldx][oldy]);
4587
4588   RemoveField(oldx, oldy);
4589   RemoveField(newx, newy);
4590
4591   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4592
4593   if (next_element != EL_UNDEFINED)
4594     Feld[oldx][oldy] = next_element;
4595
4596   DrawLevelField(oldx, oldy);
4597   DrawLevelField(newx, newy);
4598 }
4599
4600 void DrawDynamite(int x, int y)
4601 {
4602   int sx = SCREENX(x), sy = SCREENY(y);
4603   int graphic = el2img(Feld[x][y]);
4604   int frame;
4605
4606   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4607     return;
4608
4609   if (IS_WALKABLE_INSIDE(Back[x][y]))
4610     return;
4611
4612   if (Back[x][y])
4613     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4614   else if (Store[x][y])
4615     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4616
4617   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4618
4619   if (Back[x][y] || Store[x][y])
4620     DrawGraphicThruMask(sx, sy, graphic, frame);
4621   else
4622     DrawGraphic(sx, sy, graphic, frame);
4623 }
4624
4625 void CheckDynamite(int x, int y)
4626 {
4627   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4628   {
4629     MovDelay[x][y]--;
4630
4631     if (MovDelay[x][y] != 0)
4632     {
4633       DrawDynamite(x, y);
4634       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4635
4636       return;
4637     }
4638   }
4639
4640   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4641
4642   Bang(x, y);
4643 }
4644
4645 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4646 {
4647   boolean num_checked_players = 0;
4648   int i;
4649
4650   for (i = 0; i < MAX_PLAYERS; i++)
4651   {
4652     if (stored_player[i].active)
4653     {
4654       int sx = stored_player[i].jx;
4655       int sy = stored_player[i].jy;
4656
4657       if (num_checked_players == 0)
4658       {
4659         *sx1 = *sx2 = sx;
4660         *sy1 = *sy2 = sy;
4661       }
4662       else
4663       {
4664         *sx1 = MIN(*sx1, sx);
4665         *sy1 = MIN(*sy1, sy);
4666         *sx2 = MAX(*sx2, sx);
4667         *sy2 = MAX(*sy2, sy);
4668       }
4669
4670       num_checked_players++;
4671     }
4672   }
4673 }
4674
4675 static boolean checkIfAllPlayersFitToScreen_RND()
4676 {
4677   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4678
4679   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4680
4681   return (sx2 - sx1 < SCR_FIELDX &&
4682           sy2 - sy1 < SCR_FIELDY);
4683 }
4684
4685 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4686 {
4687   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4688
4689   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4690
4691   *sx = (sx1 + sx2) / 2;
4692   *sy = (sy1 + sy2) / 2;
4693 }
4694
4695 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4696                         boolean center_screen, boolean quick_relocation)
4697 {
4698   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4699   boolean no_delay = (tape.warp_forward);
4700   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4701   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4702
4703   if (quick_relocation)
4704   {
4705     int offset = game.scroll_delay_value;
4706
4707     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4708     {
4709       if (!level.shifted_relocation || center_screen)
4710       {
4711         /* quick relocation (without scrolling), with centering of screen */
4712
4713         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4714                     x > SBX_Right + MIDPOSX ? SBX_Right :
4715                     x - MIDPOSX);
4716
4717         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4718                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4719                     y - MIDPOSY);
4720       }
4721       else
4722       {
4723         /* quick relocation (without scrolling), but do not center screen */
4724
4725         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4726                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4727                                old_x - MIDPOSX);
4728
4729         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4730                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4731                                old_y - MIDPOSY);
4732
4733         int offset_x = x + (scroll_x - center_scroll_x);
4734         int offset_y = y + (scroll_y - center_scroll_y);
4735
4736         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4737                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4738                     offset_x - MIDPOSX);
4739
4740         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4741                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4742                     offset_y - MIDPOSY);
4743       }
4744     }
4745     else
4746     {
4747       /* quick relocation (without scrolling), inside visible screen area */
4748
4749       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4750           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4751         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4752
4753       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4754           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4755         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4756
4757       /* don't scroll over playfield boundaries */
4758       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4759         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4760
4761       /* don't scroll over playfield boundaries */
4762       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4763         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4764     }
4765
4766     RedrawPlayfield(TRUE, 0,0,0,0);
4767   }
4768   else
4769   {
4770 #if 1
4771     int scroll_xx, scroll_yy;
4772
4773     if (!level.shifted_relocation || center_screen)
4774     {
4775       /* visible relocation (with scrolling), with centering of screen */
4776
4777       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4778                    x > SBX_Right + MIDPOSX ? SBX_Right :
4779                    x - MIDPOSX);
4780
4781       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4782                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4783                    y - MIDPOSY);
4784     }
4785     else
4786     {
4787       /* visible relocation (with scrolling), but do not center screen */
4788
4789       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4790                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4791                              old_x - MIDPOSX);
4792
4793       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4794                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4795                              old_y - MIDPOSY);
4796
4797       int offset_x = x + (scroll_x - center_scroll_x);
4798       int offset_y = y + (scroll_y - center_scroll_y);
4799
4800       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4801                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4802                    offset_x - MIDPOSX);
4803
4804       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4805                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4806                    offset_y - MIDPOSY);
4807     }
4808
4809 #else
4810
4811     /* visible relocation (with scrolling), with centering of screen */
4812
4813     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4814                      x > SBX_Right + MIDPOSX ? SBX_Right :
4815                      x - MIDPOSX);
4816
4817     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4818                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4819                      y - MIDPOSY);
4820 #endif
4821
4822     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4823
4824     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4825     {
4826       int dx = 0, dy = 0;
4827       int fx = FX, fy = FY;
4828
4829       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4830       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4831
4832       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4833         break;
4834
4835       scroll_x -= dx;
4836       scroll_y -= dy;
4837
4838       fx += dx * TILEX / 2;
4839       fy += dy * TILEY / 2;
4840
4841       ScrollLevel(dx, dy);
4842       DrawAllPlayers();
4843
4844       /* scroll in two steps of half tile size to make things smoother */
4845       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4846       FlushDisplay();
4847       Delay(wait_delay_value);
4848
4849       /* scroll second step to align at full tile size */
4850       BackToFront();
4851       Delay(wait_delay_value);
4852     }
4853
4854     DrawAllPlayers();
4855     BackToFront();
4856     Delay(wait_delay_value);
4857   }
4858 }
4859
4860 void RelocatePlayer(int jx, int jy, int el_player_raw)
4861 {
4862   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4863   int player_nr = GET_PLAYER_NR(el_player);
4864   struct PlayerInfo *player = &stored_player[player_nr];
4865   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4866   boolean no_delay = (tape.warp_forward);
4867   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4868   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4869   int old_jx = player->jx;
4870   int old_jy = player->jy;
4871   int old_element = Feld[old_jx][old_jy];
4872   int element = Feld[jx][jy];
4873   boolean player_relocated = (old_jx != jx || old_jy != jy);
4874
4875   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4876   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4877   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4878   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4879   int leave_side_horiz = move_dir_horiz;
4880   int leave_side_vert  = move_dir_vert;
4881   int enter_side = enter_side_horiz | enter_side_vert;
4882   int leave_side = leave_side_horiz | leave_side_vert;
4883
4884   if (player->GameOver)         /* do not reanimate dead player */
4885     return;
4886
4887   if (!player_relocated)        /* no need to relocate the player */
4888     return;
4889
4890   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
4891   {
4892     RemoveField(jx, jy);        /* temporarily remove newly placed player */
4893     DrawLevelField(jx, jy);
4894   }
4895
4896   if (player->present)
4897   {
4898     while (player->MovPos)
4899     {
4900       ScrollPlayer(player, SCROLL_GO_ON);
4901       ScrollScreen(NULL, SCROLL_GO_ON);
4902
4903       AdvanceFrameAndPlayerCounters(player->index_nr);
4904
4905       DrawPlayer(player);
4906
4907       BackToFront();
4908       Delay(wait_delay_value);
4909     }
4910
4911     DrawPlayer(player);         /* needed here only to cleanup last field */
4912     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
4913
4914     player->is_moving = FALSE;
4915   }
4916
4917   if (IS_CUSTOM_ELEMENT(old_element))
4918     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
4919                                CE_LEFT_BY_PLAYER,
4920                                player->index_bit, leave_side);
4921
4922   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
4923                                       CE_PLAYER_LEAVES_X,
4924                                       player->index_bit, leave_side);
4925
4926   Feld[jx][jy] = el_player;
4927   InitPlayerField(jx, jy, el_player, TRUE);
4928
4929   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
4930   {
4931     Feld[jx][jy] = element;
4932     InitField(jx, jy, FALSE);
4933   }
4934
4935   /* only visually relocate centered player */
4936   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
4937                      FALSE, level.instant_relocation);
4938
4939   TestIfPlayerTouchesBadThing(jx, jy);
4940   TestIfPlayerTouchesCustomElement(jx, jy);
4941
4942   if (IS_CUSTOM_ELEMENT(element))
4943     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
4944                                player->index_bit, enter_side);
4945
4946   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
4947                                       player->index_bit, enter_side);
4948 }
4949
4950 void Explode(int ex, int ey, int phase, int mode)
4951 {
4952   int x, y;
4953   int last_phase;
4954   int border_element;
4955
4956   /* !!! eliminate this variable !!! */
4957   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
4958
4959   if (game.explosions_delayed)
4960   {
4961     ExplodeField[ex][ey] = mode;
4962     return;
4963   }
4964
4965   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
4966   {
4967     int center_element = Feld[ex][ey];
4968     int artwork_element, explosion_element;     /* set these values later */
4969
4970 #if 0
4971     /* --- This is only really needed (and now handled) in "Impact()". --- */
4972     /* do not explode moving elements that left the explode field in time */
4973     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
4974         center_element == EL_EMPTY &&
4975         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
4976       return;
4977 #endif
4978
4979 #if 0
4980     /* !!! at this place, the center element may be EL_BLOCKED !!! */
4981     if (mode == EX_TYPE_NORMAL ||
4982         mode == EX_TYPE_CENTER ||
4983         mode == EX_TYPE_CROSS)
4984       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
4985 #endif
4986
4987     /* remove things displayed in background while burning dynamite */
4988     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
4989       Back[ex][ey] = 0;
4990
4991     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
4992     {
4993       /* put moving element to center field (and let it explode there) */
4994       center_element = MovingOrBlocked2Element(ex, ey);
4995       RemoveMovingField(ex, ey);
4996       Feld[ex][ey] = center_element;
4997     }
4998
4999     /* now "center_element" is finally determined -- set related values now */
5000     artwork_element = center_element;           /* for custom player artwork */
5001     explosion_element = center_element;         /* for custom player artwork */
5002
5003     if (IS_PLAYER(ex, ey))
5004     {
5005       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5006
5007       artwork_element = stored_player[player_nr].artwork_element;
5008
5009       if (level.use_explosion_element[player_nr])
5010       {
5011         explosion_element = level.explosion_element[player_nr];
5012         artwork_element = explosion_element;
5013       }
5014     }
5015
5016 #if 1
5017     if (mode == EX_TYPE_NORMAL ||
5018         mode == EX_TYPE_CENTER ||
5019         mode == EX_TYPE_CROSS)
5020       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5021 #endif
5022
5023     last_phase = element_info[explosion_element].explosion_delay + 1;
5024
5025     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5026     {
5027       int xx = x - ex + 1;
5028       int yy = y - ey + 1;
5029       int element;
5030
5031       if (!IN_LEV_FIELD(x, y) ||
5032           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5033           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5034         continue;
5035
5036       element = Feld[x][y];
5037
5038       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5039       {
5040         element = MovingOrBlocked2Element(x, y);
5041
5042         if (!IS_EXPLOSION_PROOF(element))
5043           RemoveMovingField(x, y);
5044       }
5045
5046       /* indestructible elements can only explode in center (but not flames) */
5047       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5048                                            mode == EX_TYPE_BORDER)) ||
5049           element == EL_FLAMES)
5050         continue;
5051
5052       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5053          behaviour, for example when touching a yamyam that explodes to rocks
5054          with active deadly shield, a rock is created under the player !!! */
5055       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5056 #if 0
5057       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5058           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5059            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5060 #else
5061       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5062 #endif
5063       {
5064         if (IS_ACTIVE_BOMB(element))
5065         {
5066           /* re-activate things under the bomb like gate or penguin */
5067           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5068           Back[x][y] = 0;
5069         }
5070
5071         continue;
5072       }
5073
5074       /* save walkable background elements while explosion on same tile */
5075       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5076           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5077         Back[x][y] = element;
5078
5079       /* ignite explodable elements reached by other explosion */
5080       if (element == EL_EXPLOSION)
5081         element = Store2[x][y];
5082
5083       if (AmoebaNr[x][y] &&
5084           (element == EL_AMOEBA_FULL ||
5085            element == EL_BD_AMOEBA ||
5086            element == EL_AMOEBA_GROWING))
5087       {
5088         AmoebaCnt[AmoebaNr[x][y]]--;
5089         AmoebaCnt2[AmoebaNr[x][y]]--;
5090       }
5091
5092       RemoveField(x, y);
5093
5094       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5095       {
5096         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5097
5098         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5099
5100         if (PLAYERINFO(ex, ey)->use_murphy)
5101           Store[x][y] = EL_EMPTY;
5102       }
5103
5104       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5105          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5106       else if (ELEM_IS_PLAYER(center_element))
5107         Store[x][y] = EL_EMPTY;
5108       else if (center_element == EL_YAMYAM)
5109         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5110       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5111         Store[x][y] = element_info[center_element].content.e[xx][yy];
5112 #if 1
5113       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5114          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5115          otherwise) -- FIX THIS !!! */
5116       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5117         Store[x][y] = element_info[element].content.e[1][1];
5118 #else
5119       else if (!CAN_EXPLODE(element))
5120         Store[x][y] = element_info[element].content.e[1][1];
5121 #endif
5122       else
5123         Store[x][y] = EL_EMPTY;
5124
5125       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5126           center_element == EL_AMOEBA_TO_DIAMOND)
5127         Store2[x][y] = element;
5128
5129       Feld[x][y] = EL_EXPLOSION;
5130       GfxElement[x][y] = artwork_element;
5131
5132       ExplodePhase[x][y] = 1;
5133       ExplodeDelay[x][y] = last_phase;
5134
5135       Stop[x][y] = TRUE;
5136     }
5137
5138     if (center_element == EL_YAMYAM)
5139       game.yamyam_content_nr =
5140         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5141
5142     return;
5143   }
5144
5145   if (Stop[ex][ey])
5146     return;
5147
5148   x = ex;
5149   y = ey;
5150
5151   if (phase == 1)
5152     GfxFrame[x][y] = 0;         /* restart explosion animation */
5153
5154   last_phase = ExplodeDelay[x][y];
5155
5156   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5157
5158 #ifdef DEBUG
5159
5160   /* activate this even in non-DEBUG version until cause for crash in
5161      getGraphicAnimationFrame() (see below) is found and eliminated */
5162
5163 #endif
5164 #if 1
5165
5166 #if 1
5167   /* this can happen if the player leaves an explosion just in time */
5168   if (GfxElement[x][y] == EL_UNDEFINED)
5169     GfxElement[x][y] = EL_EMPTY;
5170 #else
5171   if (GfxElement[x][y] == EL_UNDEFINED)
5172   {
5173     printf("\n\n");
5174     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5175     printf("Explode(): This should never happen!\n");
5176     printf("\n\n");
5177
5178     GfxElement[x][y] = EL_EMPTY;
5179   }
5180 #endif
5181
5182 #endif
5183
5184   border_element = Store2[x][y];
5185   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5186     border_element = StorePlayer[x][y];
5187
5188   if (phase == element_info[border_element].ignition_delay ||
5189       phase == last_phase)
5190   {
5191     boolean border_explosion = FALSE;
5192
5193     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5194         !PLAYER_EXPLOSION_PROTECTED(x, y))
5195     {
5196       KillPlayerUnlessExplosionProtected(x, y);
5197       border_explosion = TRUE;
5198     }
5199     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5200     {
5201       Feld[x][y] = Store2[x][y];
5202       Store2[x][y] = 0;
5203       Bang(x, y);
5204       border_explosion = TRUE;
5205     }
5206     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5207     {
5208       AmoebeUmwandeln(x, y);
5209       Store2[x][y] = 0;
5210       border_explosion = TRUE;
5211     }
5212
5213     /* if an element just explodes due to another explosion (chain-reaction),
5214        do not immediately end the new explosion when it was the last frame of
5215        the explosion (as it would be done in the following "if"-statement!) */
5216     if (border_explosion && phase == last_phase)
5217       return;
5218   }
5219
5220   if (phase == last_phase)
5221   {
5222     int element;
5223
5224     element = Feld[x][y] = Store[x][y];
5225     Store[x][y] = Store2[x][y] = 0;
5226     GfxElement[x][y] = EL_UNDEFINED;
5227
5228     /* player can escape from explosions and might therefore be still alive */
5229     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5230         element <= EL_PLAYER_IS_EXPLODING_4)
5231     {
5232       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5233       int explosion_element = EL_PLAYER_1 + player_nr;
5234       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5235       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5236
5237       if (level.use_explosion_element[player_nr])
5238         explosion_element = level.explosion_element[player_nr];
5239
5240       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5241                     element_info[explosion_element].content.e[xx][yy]);
5242     }
5243
5244     /* restore probably existing indestructible background element */
5245     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5246       element = Feld[x][y] = Back[x][y];
5247     Back[x][y] = 0;
5248
5249     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5250     GfxDir[x][y] = MV_NONE;
5251     ChangeDelay[x][y] = 0;
5252     ChangePage[x][y] = -1;
5253
5254 #if USE_NEW_CUSTOM_VALUE
5255     CustomValue[x][y] = 0;
5256 #endif
5257
5258     InitField_WithBug2(x, y, FALSE);
5259
5260     DrawLevelField(x, y);
5261
5262     TestIfElementTouchesCustomElement(x, y);
5263
5264     if (GFX_CRUMBLED(element))
5265       DrawLevelFieldCrumbledSandNeighbours(x, y);
5266
5267     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5268       StorePlayer[x][y] = 0;
5269
5270     if (ELEM_IS_PLAYER(element))
5271       RelocatePlayer(x, y, element);
5272   }
5273   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5274   {
5275     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5276     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5277
5278     if (phase == delay)
5279       DrawLevelFieldCrumbledSand(x, y);
5280
5281     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5282     {
5283       DrawLevelElement(x, y, Back[x][y]);
5284       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5285     }
5286     else if (IS_WALKABLE_UNDER(Back[x][y]))
5287     {
5288       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5289       DrawLevelElementThruMask(x, y, Back[x][y]);
5290     }
5291     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5292       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5293   }
5294 }
5295
5296 void DynaExplode(int ex, int ey)
5297 {
5298   int i, j;
5299   int dynabomb_element = Feld[ex][ey];
5300   int dynabomb_size = 1;
5301   boolean dynabomb_xl = FALSE;
5302   struct PlayerInfo *player;
5303   static int xy[4][2] =
5304   {
5305     { 0, -1 },
5306     { -1, 0 },
5307     { +1, 0 },
5308     { 0, +1 }
5309   };
5310
5311   if (IS_ACTIVE_BOMB(dynabomb_element))
5312   {
5313     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5314     dynabomb_size = player->dynabomb_size;
5315     dynabomb_xl = player->dynabomb_xl;
5316     player->dynabombs_left++;
5317   }
5318
5319   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5320
5321   for (i = 0; i < NUM_DIRECTIONS; i++)
5322   {
5323     for (j = 1; j <= dynabomb_size; j++)
5324     {
5325       int x = ex + j * xy[i][0];
5326       int y = ey + j * xy[i][1];
5327       int element;
5328
5329       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5330         break;
5331
5332       element = Feld[x][y];
5333
5334       /* do not restart explosions of fields with active bombs */
5335       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5336         continue;
5337
5338       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5339
5340       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5341           !IS_DIGGABLE(element) && !dynabomb_xl)
5342         break;
5343     }
5344   }
5345 }
5346
5347 void Bang(int x, int y)
5348 {
5349   int element = MovingOrBlocked2Element(x, y);
5350   int explosion_type = EX_TYPE_NORMAL;
5351
5352   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5353   {
5354     struct PlayerInfo *player = PLAYERINFO(x, y);
5355
5356     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5357                             player->element_nr);
5358
5359     if (level.use_explosion_element[player->index_nr])
5360     {
5361       int explosion_element = level.explosion_element[player->index_nr];
5362
5363       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5364         explosion_type = EX_TYPE_CROSS;
5365       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5366         explosion_type = EX_TYPE_CENTER;
5367     }
5368   }
5369
5370   switch (element)
5371   {
5372     case EL_BUG:
5373     case EL_SPACESHIP:
5374     case EL_BD_BUTTERFLY:
5375     case EL_BD_FIREFLY:
5376     case EL_YAMYAM:
5377     case EL_DARK_YAMYAM:
5378     case EL_ROBOT:
5379     case EL_PACMAN:
5380     case EL_MOLE:
5381       RaiseScoreElement(element);
5382       break;
5383
5384     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5385     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5386     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5387     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5388     case EL_DYNABOMB_INCREASE_NUMBER:
5389     case EL_DYNABOMB_INCREASE_SIZE:
5390     case EL_DYNABOMB_INCREASE_POWER:
5391       explosion_type = EX_TYPE_DYNA;
5392       break;
5393
5394     case EL_DC_LANDMINE:
5395 #if 0
5396     case EL_EM_EXIT_OPEN:
5397     case EL_EM_STEEL_EXIT_OPEN:
5398 #endif
5399       explosion_type = EX_TYPE_CENTER;
5400       break;
5401
5402     case EL_PENGUIN:
5403     case EL_LAMP:
5404     case EL_LAMP_ACTIVE:
5405     case EL_AMOEBA_TO_DIAMOND:
5406       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5407         explosion_type = EX_TYPE_CENTER;
5408       break;
5409
5410     default:
5411       if (element_info[element].explosion_type == EXPLODES_CROSS)
5412         explosion_type = EX_TYPE_CROSS;
5413       else if (element_info[element].explosion_type == EXPLODES_1X1)
5414         explosion_type = EX_TYPE_CENTER;
5415       break;
5416   }
5417
5418   if (explosion_type == EX_TYPE_DYNA)
5419     DynaExplode(x, y);
5420   else
5421     Explode(x, y, EX_PHASE_START, explosion_type);
5422
5423   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5424 }
5425
5426 void SplashAcid(int x, int y)
5427 {
5428   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5429       (!IN_LEV_FIELD(x - 1, y - 2) ||
5430        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5431     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5432
5433   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5434       (!IN_LEV_FIELD(x + 1, y - 2) ||
5435        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5436     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5437
5438   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5439 }
5440
5441 static void InitBeltMovement()
5442 {
5443   static int belt_base_element[4] =
5444   {
5445     EL_CONVEYOR_BELT_1_LEFT,
5446     EL_CONVEYOR_BELT_2_LEFT,
5447     EL_CONVEYOR_BELT_3_LEFT,
5448     EL_CONVEYOR_BELT_4_LEFT
5449   };
5450   static int belt_base_active_element[4] =
5451   {
5452     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5453     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5454     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5455     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5456   };
5457
5458   int x, y, i, j;
5459
5460   /* set frame order for belt animation graphic according to belt direction */
5461   for (i = 0; i < NUM_BELTS; i++)
5462   {
5463     int belt_nr = i;
5464
5465     for (j = 0; j < NUM_BELT_PARTS; j++)
5466     {
5467       int element = belt_base_active_element[belt_nr] + j;
5468       int graphic_1 = el2img(element);
5469       int graphic_2 = el2panelimg(element);
5470
5471       if (game.belt_dir[i] == MV_LEFT)
5472       {
5473         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5474         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5475       }
5476       else
5477       {
5478         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5479         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5480       }
5481     }
5482   }
5483
5484   SCAN_PLAYFIELD(x, y)
5485   {
5486     int element = Feld[x][y];
5487
5488     for (i = 0; i < NUM_BELTS; i++)
5489     {
5490       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5491       {
5492         int e_belt_nr = getBeltNrFromBeltElement(element);
5493         int belt_nr = i;
5494
5495         if (e_belt_nr == belt_nr)
5496         {
5497           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5498
5499           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5500         }
5501       }
5502     }
5503   }
5504 }
5505
5506 static void ToggleBeltSwitch(int x, int y)
5507 {
5508   static int belt_base_element[4] =
5509   {
5510     EL_CONVEYOR_BELT_1_LEFT,
5511     EL_CONVEYOR_BELT_2_LEFT,
5512     EL_CONVEYOR_BELT_3_LEFT,
5513     EL_CONVEYOR_BELT_4_LEFT
5514   };
5515   static int belt_base_active_element[4] =
5516   {
5517     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5518     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5519     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5520     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5521   };
5522   static int belt_base_switch_element[4] =
5523   {
5524     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5525     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5526     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5527     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5528   };
5529   static int belt_move_dir[4] =
5530   {
5531     MV_LEFT,
5532     MV_NONE,
5533     MV_RIGHT,
5534     MV_NONE,
5535   };
5536
5537   int element = Feld[x][y];
5538   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5539   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5540   int belt_dir = belt_move_dir[belt_dir_nr];
5541   int xx, yy, i;
5542
5543   if (!IS_BELT_SWITCH(element))
5544     return;
5545
5546   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5547   game.belt_dir[belt_nr] = belt_dir;
5548
5549   if (belt_dir_nr == 3)
5550     belt_dir_nr = 1;
5551
5552   /* set frame order for belt animation graphic according to belt direction */
5553   for (i = 0; i < NUM_BELT_PARTS; i++)
5554   {
5555     int element = belt_base_active_element[belt_nr] + i;
5556     int graphic_1 = el2img(element);
5557     int graphic_2 = el2panelimg(element);
5558
5559     if (belt_dir == MV_LEFT)
5560     {
5561       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5562       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5563     }
5564     else
5565     {
5566       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5567       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5568     }
5569   }
5570
5571   SCAN_PLAYFIELD(xx, yy)
5572   {
5573     int element = Feld[xx][yy];
5574
5575     if (IS_BELT_SWITCH(element))
5576     {
5577       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5578
5579       if (e_belt_nr == belt_nr)
5580       {
5581         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5582         DrawLevelField(xx, yy);
5583       }
5584     }
5585     else if (IS_BELT(element) && belt_dir != MV_NONE)
5586     {
5587       int e_belt_nr = getBeltNrFromBeltElement(element);
5588
5589       if (e_belt_nr == belt_nr)
5590       {
5591         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5592
5593         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5594         DrawLevelField(xx, yy);
5595       }
5596     }
5597     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5598     {
5599       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5600
5601       if (e_belt_nr == belt_nr)
5602       {
5603         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5604
5605         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5606         DrawLevelField(xx, yy);
5607       }
5608     }
5609   }
5610 }
5611
5612 static void ToggleSwitchgateSwitch(int x, int y)
5613 {
5614   int xx, yy;
5615
5616   game.switchgate_pos = !game.switchgate_pos;
5617
5618   SCAN_PLAYFIELD(xx, yy)
5619   {
5620     int element = Feld[xx][yy];
5621
5622 #if !USE_BOTH_SWITCHGATE_SWITCHES
5623     if (element == EL_SWITCHGATE_SWITCH_UP ||
5624         element == EL_SWITCHGATE_SWITCH_DOWN)
5625     {
5626       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5627       DrawLevelField(xx, yy);
5628     }
5629     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5630              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5631     {
5632       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5633       DrawLevelField(xx, yy);
5634     }
5635 #else
5636     if (element == EL_SWITCHGATE_SWITCH_UP)
5637     {
5638       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5639       DrawLevelField(xx, yy);
5640     }
5641     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5642     {
5643       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5644       DrawLevelField(xx, yy);
5645     }
5646     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5647     {
5648       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5649       DrawLevelField(xx, yy);
5650     }
5651     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5652     {
5653       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5654       DrawLevelField(xx, yy);
5655     }
5656 #endif
5657     else if (element == EL_SWITCHGATE_OPEN ||
5658              element == EL_SWITCHGATE_OPENING)
5659     {
5660       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5661
5662       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5663     }
5664     else if (element == EL_SWITCHGATE_CLOSED ||
5665              element == EL_SWITCHGATE_CLOSING)
5666     {
5667       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5668
5669       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5670     }
5671   }
5672 }
5673
5674 static int getInvisibleActiveFromInvisibleElement(int element)
5675 {
5676   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5677           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5678           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5679           element);
5680 }
5681
5682 static int getInvisibleFromInvisibleActiveElement(int element)
5683 {
5684   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5685           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5686           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5687           element);
5688 }
5689
5690 static void RedrawAllLightSwitchesAndInvisibleElements()
5691 {
5692   int x, y;
5693
5694   SCAN_PLAYFIELD(x, y)
5695   {
5696     int element = Feld[x][y];
5697
5698     if (element == EL_LIGHT_SWITCH &&
5699         game.light_time_left > 0)
5700     {
5701       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5702       DrawLevelField(x, y);
5703     }
5704     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5705              game.light_time_left == 0)
5706     {
5707       Feld[x][y] = EL_LIGHT_SWITCH;
5708       DrawLevelField(x, y);
5709     }
5710     else if (element == EL_EMC_DRIPPER &&
5711              game.light_time_left > 0)
5712     {
5713       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5714       DrawLevelField(x, y);
5715     }
5716     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5717              game.light_time_left == 0)
5718     {
5719       Feld[x][y] = EL_EMC_DRIPPER;
5720       DrawLevelField(x, y);
5721     }
5722     else if (element == EL_INVISIBLE_STEELWALL ||
5723              element == EL_INVISIBLE_WALL ||
5724              element == EL_INVISIBLE_SAND)
5725     {
5726       if (game.light_time_left > 0)
5727         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5728
5729       DrawLevelField(x, y);
5730
5731       /* uncrumble neighbour fields, if needed */
5732       if (element == EL_INVISIBLE_SAND)
5733         DrawLevelFieldCrumbledSandNeighbours(x, y);
5734     }
5735     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5736              element == EL_INVISIBLE_WALL_ACTIVE ||
5737              element == EL_INVISIBLE_SAND_ACTIVE)
5738     {
5739       if (game.light_time_left == 0)
5740         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5741
5742       DrawLevelField(x, y);
5743
5744       /* re-crumble neighbour fields, if needed */
5745       if (element == EL_INVISIBLE_SAND)
5746         DrawLevelFieldCrumbledSandNeighbours(x, y);
5747     }
5748   }
5749 }
5750
5751 static void RedrawAllInvisibleElementsForLenses()
5752 {
5753   int x, y;
5754
5755   SCAN_PLAYFIELD(x, y)
5756   {
5757     int element = Feld[x][y];
5758
5759     if (element == EL_EMC_DRIPPER &&
5760         game.lenses_time_left > 0)
5761     {
5762       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5763       DrawLevelField(x, y);
5764     }
5765     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5766              game.lenses_time_left == 0)
5767     {
5768       Feld[x][y] = EL_EMC_DRIPPER;
5769       DrawLevelField(x, y);
5770     }
5771     else if (element == EL_INVISIBLE_STEELWALL ||
5772              element == EL_INVISIBLE_WALL ||
5773              element == EL_INVISIBLE_SAND)
5774     {
5775       if (game.lenses_time_left > 0)
5776         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5777
5778       DrawLevelField(x, y);
5779
5780       /* uncrumble neighbour fields, if needed */
5781       if (element == EL_INVISIBLE_SAND)
5782         DrawLevelFieldCrumbledSandNeighbours(x, y);
5783     }
5784     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5785              element == EL_INVISIBLE_WALL_ACTIVE ||
5786              element == EL_INVISIBLE_SAND_ACTIVE)
5787     {
5788       if (game.lenses_time_left == 0)
5789         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5790
5791       DrawLevelField(x, y);
5792
5793       /* re-crumble neighbour fields, if needed */
5794       if (element == EL_INVISIBLE_SAND)
5795         DrawLevelFieldCrumbledSandNeighbours(x, y);
5796     }
5797   }
5798 }
5799
5800 static void RedrawAllInvisibleElementsForMagnifier()
5801 {
5802   int x, y;
5803
5804   SCAN_PLAYFIELD(x, y)
5805   {
5806     int element = Feld[x][y];
5807
5808     if (element == EL_EMC_FAKE_GRASS &&
5809         game.magnify_time_left > 0)
5810     {
5811       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5812       DrawLevelField(x, y);
5813     }
5814     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5815              game.magnify_time_left == 0)
5816     {
5817       Feld[x][y] = EL_EMC_FAKE_GRASS;
5818       DrawLevelField(x, y);
5819     }
5820     else if (IS_GATE_GRAY(element) &&
5821              game.magnify_time_left > 0)
5822     {
5823       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5824                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5825                     IS_EM_GATE_GRAY(element) ?
5826                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5827                     IS_EMC_GATE_GRAY(element) ?
5828                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5829                     element);
5830       DrawLevelField(x, y);
5831     }
5832     else if (IS_GATE_GRAY_ACTIVE(element) &&
5833              game.magnify_time_left == 0)
5834     {
5835       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5836                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5837                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5838                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5839                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5840                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5841                     element);
5842       DrawLevelField(x, y);
5843     }
5844   }
5845 }
5846
5847 static void ToggleLightSwitch(int x, int y)
5848 {
5849   int element = Feld[x][y];
5850
5851   game.light_time_left =
5852     (element == EL_LIGHT_SWITCH ?
5853      level.time_light * FRAMES_PER_SECOND : 0);
5854
5855   RedrawAllLightSwitchesAndInvisibleElements();
5856 }
5857
5858 static void ActivateTimegateSwitch(int x, int y)
5859 {
5860   int xx, yy;
5861
5862   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5863
5864   SCAN_PLAYFIELD(xx, yy)
5865   {
5866     int element = Feld[xx][yy];
5867
5868     if (element == EL_TIMEGATE_CLOSED ||
5869         element == EL_TIMEGATE_CLOSING)
5870     {
5871       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5872       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5873     }
5874
5875     /*
5876     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5877     {
5878       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5879       DrawLevelField(xx, yy);
5880     }
5881     */
5882
5883   }
5884
5885 #if 1
5886   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5887                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5888 #else
5889   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5890 #endif
5891 }
5892
5893 void Impact(int x, int y)
5894 {
5895   boolean last_line = (y == lev_fieldy - 1);
5896   boolean object_hit = FALSE;
5897   boolean impact = (last_line || object_hit);
5898   int element = Feld[x][y];
5899   int smashed = EL_STEELWALL;
5900
5901   if (!last_line)       /* check if element below was hit */
5902   {
5903     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5904       return;
5905
5906     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5907                                          MovDir[x][y + 1] != MV_DOWN ||
5908                                          MovPos[x][y + 1] <= TILEY / 2));
5909
5910     /* do not smash moving elements that left the smashed field in time */
5911     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5912         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5913       object_hit = FALSE;
5914
5915 #if USE_QUICKSAND_IMPACT_BUGFIX
5916     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5917     {
5918       RemoveMovingField(x, y + 1);
5919       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5920       Feld[x][y + 2] = EL_ROCK;
5921       DrawLevelField(x, y + 2);
5922
5923       object_hit = TRUE;
5924     }
5925
5926     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5927     {
5928       RemoveMovingField(x, y + 1);
5929       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5930       Feld[x][y + 2] = EL_ROCK;
5931       DrawLevelField(x, y + 2);
5932
5933       object_hit = TRUE;
5934     }
5935 #endif
5936
5937     if (object_hit)
5938       smashed = MovingOrBlocked2Element(x, y + 1);
5939
5940     impact = (last_line || object_hit);
5941   }
5942
5943   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5944   {
5945     SplashAcid(x, y + 1);
5946     return;
5947   }
5948
5949   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5950   /* only reset graphic animation if graphic really changes after impact */
5951   if (impact &&
5952       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5953   {
5954     ResetGfxAnimation(x, y);
5955     DrawLevelField(x, y);
5956   }
5957
5958   if (impact && CAN_EXPLODE_IMPACT(element))
5959   {
5960     Bang(x, y);
5961     return;
5962   }
5963   else if (impact && element == EL_PEARL &&
5964            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5965   {
5966     ResetGfxAnimation(x, y);
5967
5968     Feld[x][y] = EL_PEARL_BREAKING;
5969     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5970     return;
5971   }
5972   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5973   {
5974     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5975
5976     return;
5977   }
5978
5979   if (impact && element == EL_AMOEBA_DROP)
5980   {
5981     if (object_hit && IS_PLAYER(x, y + 1))
5982       KillPlayerUnlessEnemyProtected(x, y + 1);
5983     else if (object_hit && smashed == EL_PENGUIN)
5984       Bang(x, y + 1);
5985     else
5986     {
5987       Feld[x][y] = EL_AMOEBA_GROWING;
5988       Store[x][y] = EL_AMOEBA_WET;
5989
5990       ResetRandomAnimationValue(x, y);
5991     }
5992     return;
5993   }
5994
5995   if (object_hit)               /* check which object was hit */
5996   {
5997     if ((CAN_PASS_MAGIC_WALL(element) && 
5998          (smashed == EL_MAGIC_WALL ||
5999           smashed == EL_BD_MAGIC_WALL)) ||
6000         (CAN_PASS_DC_MAGIC_WALL(element) &&
6001          smashed == EL_DC_MAGIC_WALL))
6002     {
6003       int xx, yy;
6004       int activated_magic_wall =
6005         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6006          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6007          EL_DC_MAGIC_WALL_ACTIVE);
6008
6009       /* activate magic wall / mill */
6010       SCAN_PLAYFIELD(xx, yy)
6011       {
6012         if (Feld[xx][yy] == smashed)
6013           Feld[xx][yy] = activated_magic_wall;
6014       }
6015
6016       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6017       game.magic_wall_active = TRUE;
6018
6019       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6020                             SND_MAGIC_WALL_ACTIVATING :
6021                             smashed == EL_BD_MAGIC_WALL ?
6022                             SND_BD_MAGIC_WALL_ACTIVATING :
6023                             SND_DC_MAGIC_WALL_ACTIVATING));
6024     }
6025
6026     if (IS_PLAYER(x, y + 1))
6027     {
6028       if (CAN_SMASH_PLAYER(element))
6029       {
6030         KillPlayerUnlessEnemyProtected(x, y + 1);
6031         return;
6032       }
6033     }
6034     else if (smashed == EL_PENGUIN)
6035     {
6036       if (CAN_SMASH_PLAYER(element))
6037       {
6038         Bang(x, y + 1);
6039         return;
6040       }
6041     }
6042     else if (element == EL_BD_DIAMOND)
6043     {
6044       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6045       {
6046         Bang(x, y + 1);
6047         return;
6048       }
6049     }
6050     else if (((element == EL_SP_INFOTRON ||
6051                element == EL_SP_ZONK) &&
6052               (smashed == EL_SP_SNIKSNAK ||
6053                smashed == EL_SP_ELECTRON ||
6054                smashed == EL_SP_DISK_ORANGE)) ||
6055              (element == EL_SP_INFOTRON &&
6056               smashed == EL_SP_DISK_YELLOW))
6057     {
6058       Bang(x, y + 1);
6059       return;
6060     }
6061     else if (CAN_SMASH_EVERYTHING(element))
6062     {
6063       if (IS_CLASSIC_ENEMY(smashed) ||
6064           CAN_EXPLODE_SMASHED(smashed))
6065       {
6066         Bang(x, y + 1);
6067         return;
6068       }
6069       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6070       {
6071         if (smashed == EL_LAMP ||
6072             smashed == EL_LAMP_ACTIVE)
6073         {
6074           Bang(x, y + 1);
6075           return;
6076         }
6077         else if (smashed == EL_NUT)
6078         {
6079           Feld[x][y + 1] = EL_NUT_BREAKING;
6080           PlayLevelSound(x, y, SND_NUT_BREAKING);
6081           RaiseScoreElement(EL_NUT);
6082           return;
6083         }
6084         else if (smashed == EL_PEARL)
6085         {
6086           ResetGfxAnimation(x, y);
6087
6088           Feld[x][y + 1] = EL_PEARL_BREAKING;
6089           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6090           return;
6091         }
6092         else if (smashed == EL_DIAMOND)
6093         {
6094           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6095           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6096           return;
6097         }
6098         else if (IS_BELT_SWITCH(smashed))
6099         {
6100           ToggleBeltSwitch(x, y + 1);
6101         }
6102         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6103                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6104                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6105                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6106         {
6107           ToggleSwitchgateSwitch(x, y + 1);
6108         }
6109         else if (smashed == EL_LIGHT_SWITCH ||
6110                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6111         {
6112           ToggleLightSwitch(x, y + 1);
6113         }
6114         else
6115         {
6116 #if 0
6117           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6118 #endif
6119
6120           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6121
6122           CheckElementChangeBySide(x, y + 1, smashed, element,
6123                                    CE_SWITCHED, CH_SIDE_TOP);
6124           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6125                                             CH_SIDE_TOP);
6126         }
6127       }
6128       else
6129       {
6130         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6131       }
6132     }
6133   }
6134
6135   /* play sound of magic wall / mill */
6136   if (!last_line &&
6137       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6138        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6139        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6140   {
6141     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6142       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6143     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6144       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6145     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6146       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6147
6148     return;
6149   }
6150
6151   /* play sound of object that hits the ground */
6152   if (last_line || object_hit)
6153     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6154 }
6155
6156 inline static void TurnRoundExt(int x, int y)
6157 {
6158   static struct
6159   {
6160     int dx, dy;
6161   } move_xy[] =
6162   {
6163     {  0,  0 },
6164     { -1,  0 },
6165     { +1,  0 },
6166     {  0,  0 },
6167     {  0, -1 },
6168     {  0,  0 }, { 0, 0 }, { 0, 0 },
6169     {  0, +1 }
6170   };
6171   static struct
6172   {
6173     int left, right, back;
6174   } turn[] =
6175   {
6176     { 0,        0,              0        },
6177     { MV_DOWN,  MV_UP,          MV_RIGHT },
6178     { MV_UP,    MV_DOWN,        MV_LEFT  },
6179     { 0,        0,              0        },
6180     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6181     { 0,        0,              0        },
6182     { 0,        0,              0        },
6183     { 0,        0,              0        },
6184     { MV_RIGHT, MV_LEFT,        MV_UP    }
6185   };
6186
6187   int element = Feld[x][y];
6188   int move_pattern = element_info[element].move_pattern;
6189
6190   int old_move_dir = MovDir[x][y];
6191   int left_dir  = turn[old_move_dir].left;
6192   int right_dir = turn[old_move_dir].right;
6193   int back_dir  = turn[old_move_dir].back;
6194
6195   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6196   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6197   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6198   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6199
6200   int left_x  = x + left_dx,  left_y  = y + left_dy;
6201   int right_x = x + right_dx, right_y = y + right_dy;
6202   int move_x  = x + move_dx,  move_y  = y + move_dy;
6203
6204   int xx, yy;
6205
6206   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6207   {
6208     TestIfBadThingTouchesOtherBadThing(x, y);
6209
6210     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6211       MovDir[x][y] = right_dir;
6212     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6213       MovDir[x][y] = left_dir;
6214
6215     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6216       MovDelay[x][y] = 9;
6217     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6218       MovDelay[x][y] = 1;
6219   }
6220   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6221   {
6222     TestIfBadThingTouchesOtherBadThing(x, y);
6223
6224     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6225       MovDir[x][y] = left_dir;
6226     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6227       MovDir[x][y] = right_dir;
6228
6229     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6230       MovDelay[x][y] = 9;
6231     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6232       MovDelay[x][y] = 1;
6233   }
6234   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6235   {
6236     TestIfBadThingTouchesOtherBadThing(x, y);
6237
6238     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6239       MovDir[x][y] = left_dir;
6240     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6241       MovDir[x][y] = right_dir;
6242
6243     if (MovDir[x][y] != old_move_dir)
6244       MovDelay[x][y] = 9;
6245   }
6246   else if (element == EL_YAMYAM)
6247   {
6248     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6249     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6250
6251     if (can_turn_left && can_turn_right)
6252       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6253     else if (can_turn_left)
6254       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6255     else if (can_turn_right)
6256       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6257     else
6258       MovDir[x][y] = back_dir;
6259
6260     MovDelay[x][y] = 16 + 16 * RND(3);
6261   }
6262   else if (element == EL_DARK_YAMYAM)
6263   {
6264     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6265                                                          left_x, left_y);
6266     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6267                                                          right_x, right_y);
6268
6269     if (can_turn_left && can_turn_right)
6270       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6271     else if (can_turn_left)
6272       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6273     else if (can_turn_right)
6274       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6275     else
6276       MovDir[x][y] = back_dir;
6277
6278     MovDelay[x][y] = 16 + 16 * RND(3);
6279   }
6280   else if (element == EL_PACMAN)
6281   {
6282     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6283     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6284
6285     if (can_turn_left && can_turn_right)
6286       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6287     else if (can_turn_left)
6288       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6289     else if (can_turn_right)
6290       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6291     else
6292       MovDir[x][y] = back_dir;
6293
6294     MovDelay[x][y] = 6 + RND(40);
6295   }
6296   else if (element == EL_PIG)
6297   {
6298     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6299     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6300     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6301     boolean should_turn_left, should_turn_right, should_move_on;
6302     int rnd_value = 24;
6303     int rnd = RND(rnd_value);
6304
6305     should_turn_left = (can_turn_left &&
6306                         (!can_move_on ||
6307                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6308                                                    y + back_dy + left_dy)));
6309     should_turn_right = (can_turn_right &&
6310                          (!can_move_on ||
6311                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6312                                                     y + back_dy + right_dy)));
6313     should_move_on = (can_move_on &&
6314                       (!can_turn_left ||
6315                        !can_turn_right ||
6316                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6317                                                  y + move_dy + left_dy) ||
6318                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6319                                                  y + move_dy + right_dy)));
6320
6321     if (should_turn_left || should_turn_right || should_move_on)
6322     {
6323       if (should_turn_left && should_turn_right && should_move_on)
6324         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6325                         rnd < 2 * rnd_value / 3 ? right_dir :
6326                         old_move_dir);
6327       else if (should_turn_left && should_turn_right)
6328         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6329       else if (should_turn_left && should_move_on)
6330         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6331       else if (should_turn_right && should_move_on)
6332         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6333       else if (should_turn_left)
6334         MovDir[x][y] = left_dir;
6335       else if (should_turn_right)
6336         MovDir[x][y] = right_dir;
6337       else if (should_move_on)
6338         MovDir[x][y] = old_move_dir;
6339     }
6340     else if (can_move_on && rnd > rnd_value / 8)
6341       MovDir[x][y] = old_move_dir;
6342     else if (can_turn_left && can_turn_right)
6343       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6344     else if (can_turn_left && rnd > rnd_value / 8)
6345       MovDir[x][y] = left_dir;
6346     else if (can_turn_right && rnd > rnd_value/8)
6347       MovDir[x][y] = right_dir;
6348     else
6349       MovDir[x][y] = back_dir;
6350
6351     xx = x + move_xy[MovDir[x][y]].dx;
6352     yy = y + move_xy[MovDir[x][y]].dy;
6353
6354     if (!IN_LEV_FIELD(xx, yy) ||
6355         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6356       MovDir[x][y] = old_move_dir;
6357
6358     MovDelay[x][y] = 0;
6359   }
6360   else if (element == EL_DRAGON)
6361   {
6362     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6363     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6364     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6365     int rnd_value = 24;
6366     int rnd = RND(rnd_value);
6367
6368     if (can_move_on && rnd > rnd_value / 8)
6369       MovDir[x][y] = old_move_dir;
6370     else if (can_turn_left && can_turn_right)
6371       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6372     else if (can_turn_left && rnd > rnd_value / 8)
6373       MovDir[x][y] = left_dir;
6374     else if (can_turn_right && rnd > rnd_value / 8)
6375       MovDir[x][y] = right_dir;
6376     else
6377       MovDir[x][y] = back_dir;
6378
6379     xx = x + move_xy[MovDir[x][y]].dx;
6380     yy = y + move_xy[MovDir[x][y]].dy;
6381
6382     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6383       MovDir[x][y] = old_move_dir;
6384
6385     MovDelay[x][y] = 0;
6386   }
6387   else if (element == EL_MOLE)
6388   {
6389     boolean can_move_on =
6390       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6391                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6392                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6393     if (!can_move_on)
6394     {
6395       boolean can_turn_left =
6396         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6397                               IS_AMOEBOID(Feld[left_x][left_y])));
6398
6399       boolean can_turn_right =
6400         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6401                               IS_AMOEBOID(Feld[right_x][right_y])));
6402
6403       if (can_turn_left && can_turn_right)
6404         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6405       else if (can_turn_left)
6406         MovDir[x][y] = left_dir;
6407       else
6408         MovDir[x][y] = right_dir;
6409     }
6410
6411     if (MovDir[x][y] != old_move_dir)
6412       MovDelay[x][y] = 9;
6413   }
6414   else if (element == EL_BALLOON)
6415   {
6416     MovDir[x][y] = game.wind_direction;
6417     MovDelay[x][y] = 0;
6418   }
6419   else if (element == EL_SPRING)
6420   {
6421 #if USE_NEW_SPRING_BUMPER
6422     if (MovDir[x][y] & MV_HORIZONTAL)
6423     {
6424       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6425           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6426       {
6427         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6428         ResetGfxAnimation(move_x, move_y);
6429         DrawLevelField(move_x, move_y);
6430
6431         MovDir[x][y] = back_dir;
6432       }
6433       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6434                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6435         MovDir[x][y] = MV_NONE;
6436     }
6437 #else
6438     if (MovDir[x][y] & MV_HORIZONTAL &&
6439         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6440          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6441       MovDir[x][y] = MV_NONE;
6442 #endif
6443
6444     MovDelay[x][y] = 0;
6445   }
6446   else if (element == EL_ROBOT ||
6447            element == EL_SATELLITE ||
6448            element == EL_PENGUIN ||
6449            element == EL_EMC_ANDROID)
6450   {
6451     int attr_x = -1, attr_y = -1;
6452
6453     if (AllPlayersGone)
6454     {
6455       attr_x = ExitX;
6456       attr_y = ExitY;
6457     }
6458     else
6459     {
6460       int i;
6461
6462       for (i = 0; i < MAX_PLAYERS; i++)
6463       {
6464         struct PlayerInfo *player = &stored_player[i];
6465         int jx = player->jx, jy = player->jy;
6466
6467         if (!player->active)
6468           continue;
6469
6470         if (attr_x == -1 ||
6471             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6472         {
6473           attr_x = jx;
6474           attr_y = jy;
6475         }
6476       }
6477     }
6478
6479     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6480         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6481          game.engine_version < VERSION_IDENT(3,1,0,0)))
6482     {
6483       attr_x = ZX;
6484       attr_y = ZY;
6485     }
6486
6487     if (element == EL_PENGUIN)
6488     {
6489       int i;
6490       static int xy[4][2] =
6491       {
6492         { 0, -1 },
6493         { -1, 0 },
6494         { +1, 0 },
6495         { 0, +1 }
6496       };
6497
6498       for (i = 0; i < NUM_DIRECTIONS; i++)
6499       {
6500         int ex = x + xy[i][0];
6501         int ey = y + xy[i][1];
6502
6503         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6504                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6505                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6506                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6507         {
6508           attr_x = ex;
6509           attr_y = ey;
6510           break;
6511         }
6512       }
6513     }
6514
6515     MovDir[x][y] = MV_NONE;
6516     if (attr_x < x)
6517       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6518     else if (attr_x > x)
6519       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6520     if (attr_y < y)
6521       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6522     else if (attr_y > y)
6523       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6524
6525     if (element == EL_ROBOT)
6526     {
6527       int newx, newy;
6528
6529       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6530         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6531       Moving2Blocked(x, y, &newx, &newy);
6532
6533       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6534         MovDelay[x][y] = 8 + 8 * !RND(3);
6535       else
6536         MovDelay[x][y] = 16;
6537     }
6538     else if (element == EL_PENGUIN)
6539     {
6540       int newx, newy;
6541
6542       MovDelay[x][y] = 1;
6543
6544       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6545       {
6546         boolean first_horiz = RND(2);
6547         int new_move_dir = MovDir[x][y];
6548
6549         MovDir[x][y] =
6550           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6551         Moving2Blocked(x, y, &newx, &newy);
6552
6553         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6554           return;
6555
6556         MovDir[x][y] =
6557           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6558         Moving2Blocked(x, y, &newx, &newy);
6559
6560         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6561           return;
6562
6563         MovDir[x][y] = old_move_dir;
6564         return;
6565       }
6566     }
6567     else if (element == EL_SATELLITE)
6568     {
6569       int newx, newy;
6570
6571       MovDelay[x][y] = 1;
6572
6573       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6574       {
6575         boolean first_horiz = RND(2);
6576         int new_move_dir = MovDir[x][y];
6577
6578         MovDir[x][y] =
6579           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6580         Moving2Blocked(x, y, &newx, &newy);
6581
6582         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6583           return;
6584
6585         MovDir[x][y] =
6586           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6587         Moving2Blocked(x, y, &newx, &newy);
6588
6589         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6590           return;
6591
6592         MovDir[x][y] = old_move_dir;
6593         return;
6594       }
6595     }
6596     else if (element == EL_EMC_ANDROID)
6597     {
6598       static int check_pos[16] =
6599       {
6600         -1,             /*  0 => (invalid)          */
6601         7,              /*  1 => MV_LEFT            */
6602         3,              /*  2 => MV_RIGHT           */
6603         -1,             /*  3 => (invalid)          */
6604         1,              /*  4 =>            MV_UP   */
6605         0,              /*  5 => MV_LEFT  | MV_UP   */
6606         2,              /*  6 => MV_RIGHT | MV_UP   */
6607         -1,             /*  7 => (invalid)          */
6608         5,              /*  8 =>            MV_DOWN */
6609         6,              /*  9 => MV_LEFT  | MV_DOWN */
6610         4,              /* 10 => MV_RIGHT | MV_DOWN */
6611         -1,             /* 11 => (invalid)          */
6612         -1,             /* 12 => (invalid)          */
6613         -1,             /* 13 => (invalid)          */
6614         -1,             /* 14 => (invalid)          */
6615         -1,             /* 15 => (invalid)          */
6616       };
6617       static struct
6618       {
6619         int dx, dy;
6620         int dir;
6621       } check_xy[8] =
6622       {
6623         { -1, -1,       MV_LEFT  | MV_UP   },
6624         {  0, -1,                  MV_UP   },
6625         { +1, -1,       MV_RIGHT | MV_UP   },
6626         { +1,  0,       MV_RIGHT           },
6627         { +1, +1,       MV_RIGHT | MV_DOWN },
6628         {  0, +1,                  MV_DOWN },
6629         { -1, +1,       MV_LEFT  | MV_DOWN },
6630         { -1,  0,       MV_LEFT            },
6631       };
6632       int start_pos, check_order;
6633       boolean can_clone = FALSE;
6634       int i;
6635
6636       /* check if there is any free field around current position */
6637       for (i = 0; i < 8; i++)
6638       {
6639         int newx = x + check_xy[i].dx;
6640         int newy = y + check_xy[i].dy;
6641
6642         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6643         {
6644           can_clone = TRUE;
6645
6646           break;
6647         }
6648       }
6649
6650       if (can_clone)            /* randomly find an element to clone */
6651       {
6652         can_clone = FALSE;
6653
6654         start_pos = check_pos[RND(8)];
6655         check_order = (RND(2) ? -1 : +1);
6656
6657         for (i = 0; i < 8; i++)
6658         {
6659           int pos_raw = start_pos + i * check_order;
6660           int pos = (pos_raw + 8) % 8;
6661           int newx = x + check_xy[pos].dx;
6662           int newy = y + check_xy[pos].dy;
6663
6664           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6665           {
6666             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6667             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6668
6669             Store[x][y] = Feld[newx][newy];
6670
6671             can_clone = TRUE;
6672
6673             break;
6674           }
6675         }
6676       }
6677
6678       if (can_clone)            /* randomly find a direction to move */
6679       {
6680         can_clone = FALSE;
6681
6682         start_pos = check_pos[RND(8)];
6683         check_order = (RND(2) ? -1 : +1);
6684
6685         for (i = 0; i < 8; i++)
6686         {
6687           int pos_raw = start_pos + i * check_order;
6688           int pos = (pos_raw + 8) % 8;
6689           int newx = x + check_xy[pos].dx;
6690           int newy = y + check_xy[pos].dy;
6691           int new_move_dir = check_xy[pos].dir;
6692
6693           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6694           {
6695             MovDir[x][y] = new_move_dir;
6696             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6697
6698             can_clone = TRUE;
6699
6700             break;
6701           }
6702         }
6703       }
6704
6705       if (can_clone)            /* cloning and moving successful */
6706         return;
6707
6708       /* cannot clone -- try to move towards player */
6709
6710       start_pos = check_pos[MovDir[x][y] & 0x0f];
6711       check_order = (RND(2) ? -1 : +1);
6712
6713       for (i = 0; i < 3; i++)
6714       {
6715         /* first check start_pos, then previous/next or (next/previous) pos */
6716         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6717         int pos = (pos_raw + 8) % 8;
6718         int newx = x + check_xy[pos].dx;
6719         int newy = y + check_xy[pos].dy;
6720         int new_move_dir = check_xy[pos].dir;
6721
6722         if (IS_PLAYER(newx, newy))
6723           break;
6724
6725         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6726         {
6727           MovDir[x][y] = new_move_dir;
6728           MovDelay[x][y] = level.android_move_time * 8 + 1;
6729
6730           break;
6731         }
6732       }
6733     }
6734   }
6735   else if (move_pattern == MV_TURNING_LEFT ||
6736            move_pattern == MV_TURNING_RIGHT ||
6737            move_pattern == MV_TURNING_LEFT_RIGHT ||
6738            move_pattern == MV_TURNING_RIGHT_LEFT ||
6739            move_pattern == MV_TURNING_RANDOM ||
6740            move_pattern == MV_ALL_DIRECTIONS)
6741   {
6742     boolean can_turn_left =
6743       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6744     boolean can_turn_right =
6745       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6746
6747     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6748       return;
6749
6750     if (move_pattern == MV_TURNING_LEFT)
6751       MovDir[x][y] = left_dir;
6752     else if (move_pattern == MV_TURNING_RIGHT)
6753       MovDir[x][y] = right_dir;
6754     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6755       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6756     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6757       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6758     else if (move_pattern == MV_TURNING_RANDOM)
6759       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6760                       can_turn_right && !can_turn_left ? right_dir :
6761                       RND(2) ? left_dir : right_dir);
6762     else if (can_turn_left && can_turn_right)
6763       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6764     else if (can_turn_left)
6765       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6766     else if (can_turn_right)
6767       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6768     else
6769       MovDir[x][y] = back_dir;
6770
6771     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6772   }
6773   else if (move_pattern == MV_HORIZONTAL ||
6774            move_pattern == MV_VERTICAL)
6775   {
6776     if (move_pattern & old_move_dir)
6777       MovDir[x][y] = back_dir;
6778     else if (move_pattern == MV_HORIZONTAL)
6779       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6780     else if (move_pattern == MV_VERTICAL)
6781       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6782
6783     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6784   }
6785   else if (move_pattern & MV_ANY_DIRECTION)
6786   {
6787     MovDir[x][y] = move_pattern;
6788     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6789   }
6790   else if (move_pattern & MV_WIND_DIRECTION)
6791   {
6792     MovDir[x][y] = game.wind_direction;
6793     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6794   }
6795   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6796   {
6797     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6798       MovDir[x][y] = left_dir;
6799     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6800       MovDir[x][y] = right_dir;
6801
6802     if (MovDir[x][y] != old_move_dir)
6803       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6804   }
6805   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6806   {
6807     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6808       MovDir[x][y] = right_dir;
6809     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6810       MovDir[x][y] = left_dir;
6811
6812     if (MovDir[x][y] != old_move_dir)
6813       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6814   }
6815   else if (move_pattern == MV_TOWARDS_PLAYER ||
6816            move_pattern == MV_AWAY_FROM_PLAYER)
6817   {
6818     int attr_x = -1, attr_y = -1;
6819     int newx, newy;
6820     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6821
6822     if (AllPlayersGone)
6823     {
6824       attr_x = ExitX;
6825       attr_y = ExitY;
6826     }
6827     else
6828     {
6829       int i;
6830
6831       for (i = 0; i < MAX_PLAYERS; i++)
6832       {
6833         struct PlayerInfo *player = &stored_player[i];
6834         int jx = player->jx, jy = player->jy;
6835
6836         if (!player->active)
6837           continue;
6838
6839         if (attr_x == -1 ||
6840             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6841         {
6842           attr_x = jx;
6843           attr_y = jy;
6844         }
6845       }
6846     }
6847
6848     MovDir[x][y] = MV_NONE;
6849     if (attr_x < x)
6850       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6851     else if (attr_x > x)
6852       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6853     if (attr_y < y)
6854       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6855     else if (attr_y > y)
6856       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6857
6858     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6859
6860     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6861     {
6862       boolean first_horiz = RND(2);
6863       int new_move_dir = MovDir[x][y];
6864
6865       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6866       {
6867         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6868         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6869
6870         return;
6871       }
6872
6873       MovDir[x][y] =
6874         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6875       Moving2Blocked(x, y, &newx, &newy);
6876
6877       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6878         return;
6879
6880       MovDir[x][y] =
6881         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6882       Moving2Blocked(x, y, &newx, &newy);
6883
6884       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6885         return;
6886
6887       MovDir[x][y] = old_move_dir;
6888     }
6889   }
6890   else if (move_pattern == MV_WHEN_PUSHED ||
6891            move_pattern == MV_WHEN_DROPPED)
6892   {
6893     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6894       MovDir[x][y] = MV_NONE;
6895
6896     MovDelay[x][y] = 0;
6897   }
6898   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6899   {
6900     static int test_xy[7][2] =
6901     {
6902       { 0, -1 },
6903       { -1, 0 },
6904       { +1, 0 },
6905       { 0, +1 },
6906       { 0, -1 },
6907       { -1, 0 },
6908       { +1, 0 },
6909     };
6910     static int test_dir[7] =
6911     {
6912       MV_UP,
6913       MV_LEFT,
6914       MV_RIGHT,
6915       MV_DOWN,
6916       MV_UP,
6917       MV_LEFT,
6918       MV_RIGHT,
6919     };
6920     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6921     int move_preference = -1000000;     /* start with very low preference */
6922     int new_move_dir = MV_NONE;
6923     int start_test = RND(4);
6924     int i;
6925
6926     for (i = 0; i < NUM_DIRECTIONS; i++)
6927     {
6928       int move_dir = test_dir[start_test + i];
6929       int move_dir_preference;
6930
6931       xx = x + test_xy[start_test + i][0];
6932       yy = y + test_xy[start_test + i][1];
6933
6934       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6935           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6936       {
6937         new_move_dir = move_dir;
6938
6939         break;
6940       }
6941
6942       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6943         continue;
6944
6945       move_dir_preference = -1 * RunnerVisit[xx][yy];
6946       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6947         move_dir_preference = PlayerVisit[xx][yy];
6948
6949       if (move_dir_preference > move_preference)
6950       {
6951         /* prefer field that has not been visited for the longest time */
6952         move_preference = move_dir_preference;
6953         new_move_dir = move_dir;
6954       }
6955       else if (move_dir_preference == move_preference &&
6956                move_dir == old_move_dir)
6957       {
6958         /* prefer last direction when all directions are preferred equally */
6959         move_preference = move_dir_preference;
6960         new_move_dir = move_dir;
6961       }
6962     }
6963
6964     MovDir[x][y] = new_move_dir;
6965     if (old_move_dir != new_move_dir)
6966       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6967   }
6968 }
6969
6970 static void TurnRound(int x, int y)
6971 {
6972   int direction = MovDir[x][y];
6973
6974   TurnRoundExt(x, y);
6975
6976   GfxDir[x][y] = MovDir[x][y];
6977
6978   if (direction != MovDir[x][y])
6979     GfxFrame[x][y] = 0;
6980
6981   if (MovDelay[x][y])
6982     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6983
6984   ResetGfxFrame(x, y, FALSE);
6985 }
6986
6987 static boolean JustBeingPushed(int x, int y)
6988 {
6989   int i;
6990
6991   for (i = 0; i < MAX_PLAYERS; i++)
6992   {
6993     struct PlayerInfo *player = &stored_player[i];
6994
6995     if (player->active && player->is_pushing && player->MovPos)
6996     {
6997       int next_jx = player->jx + (player->jx - player->last_jx);
6998       int next_jy = player->jy + (player->jy - player->last_jy);
6999
7000       if (x == next_jx && y == next_jy)
7001         return TRUE;
7002     }
7003   }
7004
7005   return FALSE;
7006 }
7007
7008 void StartMoving(int x, int y)
7009 {
7010   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7011   int element = Feld[x][y];
7012
7013   if (Stop[x][y])
7014     return;
7015
7016   if (MovDelay[x][y] == 0)
7017     GfxAction[x][y] = ACTION_DEFAULT;
7018
7019   if (CAN_FALL(element) && y < lev_fieldy - 1)
7020   {
7021     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7022         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7023       if (JustBeingPushed(x, y))
7024         return;
7025
7026     if (element == EL_QUICKSAND_FULL)
7027     {
7028       if (IS_FREE(x, y + 1))
7029       {
7030         InitMovingField(x, y, MV_DOWN);
7031         started_moving = TRUE;
7032
7033         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7034 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7035         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7036           Store[x][y] = EL_ROCK;
7037 #else
7038         Store[x][y] = EL_ROCK;
7039 #endif
7040
7041         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7042       }
7043       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7044       {
7045         if (!MovDelay[x][y])
7046           MovDelay[x][y] = TILEY + 1;
7047
7048         if (MovDelay[x][y])
7049         {
7050           MovDelay[x][y]--;
7051           if (MovDelay[x][y])
7052             return;
7053         }
7054
7055         Feld[x][y] = EL_QUICKSAND_EMPTY;
7056         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7057         Store[x][y + 1] = Store[x][y];
7058         Store[x][y] = 0;
7059
7060         PlayLevelSoundAction(x, y, ACTION_FILLING);
7061       }
7062     }
7063     else if (element == EL_QUICKSAND_FAST_FULL)
7064     {
7065       if (IS_FREE(x, y + 1))
7066       {
7067         InitMovingField(x, y, MV_DOWN);
7068         started_moving = TRUE;
7069
7070         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7071 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7072         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7073           Store[x][y] = EL_ROCK;
7074 #else
7075         Store[x][y] = EL_ROCK;
7076 #endif
7077
7078         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7079       }
7080       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7081       {
7082         if (!MovDelay[x][y])
7083           MovDelay[x][y] = TILEY + 1;
7084
7085         if (MovDelay[x][y])
7086         {
7087           MovDelay[x][y]--;
7088           if (MovDelay[x][y])
7089             return;
7090         }
7091
7092         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7093         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7094         Store[x][y + 1] = Store[x][y];
7095         Store[x][y] = 0;
7096
7097         PlayLevelSoundAction(x, y, ACTION_FILLING);
7098       }
7099     }
7100     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7101              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7102     {
7103       InitMovingField(x, y, MV_DOWN);
7104       started_moving = TRUE;
7105
7106       Feld[x][y] = EL_QUICKSAND_FILLING;
7107       Store[x][y] = element;
7108
7109       PlayLevelSoundAction(x, y, ACTION_FILLING);
7110     }
7111     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7112              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7113     {
7114       InitMovingField(x, y, MV_DOWN);
7115       started_moving = TRUE;
7116
7117       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7118       Store[x][y] = element;
7119
7120       PlayLevelSoundAction(x, y, ACTION_FILLING);
7121     }
7122     else if (element == EL_MAGIC_WALL_FULL)
7123     {
7124       if (IS_FREE(x, y + 1))
7125       {
7126         InitMovingField(x, y, MV_DOWN);
7127         started_moving = TRUE;
7128
7129         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7130         Store[x][y] = EL_CHANGED(Store[x][y]);
7131       }
7132       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7133       {
7134         if (!MovDelay[x][y])
7135           MovDelay[x][y] = TILEY/4 + 1;
7136
7137         if (MovDelay[x][y])
7138         {
7139           MovDelay[x][y]--;
7140           if (MovDelay[x][y])
7141             return;
7142         }
7143
7144         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7145         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7146         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7147         Store[x][y] = 0;
7148       }
7149     }
7150     else if (element == EL_BD_MAGIC_WALL_FULL)
7151     {
7152       if (IS_FREE(x, y + 1))
7153       {
7154         InitMovingField(x, y, MV_DOWN);
7155         started_moving = TRUE;
7156
7157         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7158         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7159       }
7160       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7161       {
7162         if (!MovDelay[x][y])
7163           MovDelay[x][y] = TILEY/4 + 1;
7164
7165         if (MovDelay[x][y])
7166         {
7167           MovDelay[x][y]--;
7168           if (MovDelay[x][y])
7169             return;
7170         }
7171
7172         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7173         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7174         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7175         Store[x][y] = 0;
7176       }
7177     }
7178     else if (element == EL_DC_MAGIC_WALL_FULL)
7179     {
7180       if (IS_FREE(x, y + 1))
7181       {
7182         InitMovingField(x, y, MV_DOWN);
7183         started_moving = TRUE;
7184
7185         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7186         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7187       }
7188       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7189       {
7190         if (!MovDelay[x][y])
7191           MovDelay[x][y] = TILEY/4 + 1;
7192
7193         if (MovDelay[x][y])
7194         {
7195           MovDelay[x][y]--;
7196           if (MovDelay[x][y])
7197             return;
7198         }
7199
7200         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7201         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7202         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7203         Store[x][y] = 0;
7204       }
7205     }
7206     else if ((CAN_PASS_MAGIC_WALL(element) &&
7207               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7208                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7209              (CAN_PASS_DC_MAGIC_WALL(element) &&
7210               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7211
7212     {
7213       InitMovingField(x, y, MV_DOWN);
7214       started_moving = TRUE;
7215
7216       Feld[x][y] =
7217         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7218          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7219          EL_DC_MAGIC_WALL_FILLING);
7220       Store[x][y] = element;
7221     }
7222     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7223     {
7224       SplashAcid(x, y + 1);
7225
7226       InitMovingField(x, y, MV_DOWN);
7227       started_moving = TRUE;
7228
7229       Store[x][y] = EL_ACID;
7230     }
7231     else if (
7232 #if USE_FIX_IMPACT_COLLISION
7233              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7234               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7235 #else
7236              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7237               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7238 #endif
7239              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7240               CAN_FALL(element) && WasJustFalling[x][y] &&
7241               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7242
7243              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7244               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7245               (Feld[x][y + 1] == EL_BLOCKED)))
7246     {
7247       /* this is needed for a special case not covered by calling "Impact()"
7248          from "ContinueMoving()": if an element moves to a tile directly below
7249          another element which was just falling on that tile (which was empty
7250          in the previous frame), the falling element above would just stop
7251          instead of smashing the element below (in previous version, the above
7252          element was just checked for "moving" instead of "falling", resulting
7253          in incorrect smashes caused by horizontal movement of the above
7254          element; also, the case of the player being the element to smash was
7255          simply not covered here... :-/ ) */
7256
7257       CheckCollision[x][y] = 0;
7258       CheckImpact[x][y] = 0;
7259
7260       Impact(x, y);
7261     }
7262     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7263     {
7264       if (MovDir[x][y] == MV_NONE)
7265       {
7266         InitMovingField(x, y, MV_DOWN);
7267         started_moving = TRUE;
7268       }
7269     }
7270     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7271     {
7272       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7273         MovDir[x][y] = MV_DOWN;
7274
7275       InitMovingField(x, y, MV_DOWN);
7276       started_moving = TRUE;
7277     }
7278     else if (element == EL_AMOEBA_DROP)
7279     {
7280       Feld[x][y] = EL_AMOEBA_GROWING;
7281       Store[x][y] = EL_AMOEBA_WET;
7282     }
7283     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7284               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7285              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7286              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7287     {
7288       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7289                                 (IS_FREE(x - 1, y + 1) ||
7290                                  Feld[x - 1][y + 1] == EL_ACID));
7291       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7292                                 (IS_FREE(x + 1, y + 1) ||
7293                                  Feld[x + 1][y + 1] == EL_ACID));
7294       boolean can_fall_any  = (can_fall_left || can_fall_right);
7295       boolean can_fall_both = (can_fall_left && can_fall_right);
7296       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7297
7298 #if USE_NEW_ALL_SLIPPERY
7299       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7300       {
7301         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7302           can_fall_right = FALSE;
7303         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7304           can_fall_left = FALSE;
7305         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7306           can_fall_right = FALSE;
7307         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7308           can_fall_left = FALSE;
7309
7310         can_fall_any  = (can_fall_left || can_fall_right);
7311         can_fall_both = FALSE;
7312       }
7313 #else
7314       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7315       {
7316         if (slippery_type == SLIPPERY_ONLY_LEFT)
7317           can_fall_right = FALSE;
7318         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7319           can_fall_left = FALSE;
7320         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7321           can_fall_right = FALSE;
7322         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7323           can_fall_left = FALSE;
7324
7325         can_fall_any  = (can_fall_left || can_fall_right);
7326         can_fall_both = (can_fall_left && can_fall_right);
7327       }
7328 #endif
7329
7330 #if USE_NEW_ALL_SLIPPERY
7331 #else
7332 #if USE_NEW_SP_SLIPPERY
7333       /* !!! better use the same properties as for custom elements here !!! */
7334       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7335                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7336       {
7337         can_fall_right = FALSE;         /* slip down on left side */
7338         can_fall_both = FALSE;
7339       }
7340 #endif
7341 #endif
7342
7343 #if USE_NEW_ALL_SLIPPERY
7344       if (can_fall_both)
7345       {
7346         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7347           can_fall_right = FALSE;       /* slip down on left side */
7348         else
7349           can_fall_left = !(can_fall_right = RND(2));
7350
7351         can_fall_both = FALSE;
7352       }
7353 #else
7354       if (can_fall_both)
7355       {
7356         if (game.emulation == EMU_BOULDERDASH ||
7357             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7358           can_fall_right = FALSE;       /* slip down on left side */
7359         else
7360           can_fall_left = !(can_fall_right = RND(2));
7361
7362         can_fall_both = FALSE;
7363       }
7364 #endif
7365
7366       if (can_fall_any)
7367       {
7368         /* if not determined otherwise, prefer left side for slipping down */
7369         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7370         started_moving = TRUE;
7371       }
7372     }
7373 #if 0
7374     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7375 #else
7376     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7377 #endif
7378     {
7379       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7380       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7381       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7382       int belt_dir = game.belt_dir[belt_nr];
7383
7384       if ((belt_dir == MV_LEFT  && left_is_free) ||
7385           (belt_dir == MV_RIGHT && right_is_free))
7386       {
7387         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7388
7389         InitMovingField(x, y, belt_dir);
7390         started_moving = TRUE;
7391
7392         Pushed[x][y] = TRUE;
7393         Pushed[nextx][y] = TRUE;
7394
7395         GfxAction[x][y] = ACTION_DEFAULT;
7396       }
7397       else
7398       {
7399         MovDir[x][y] = 0;       /* if element was moving, stop it */
7400       }
7401     }
7402   }
7403
7404   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7405 #if 0
7406   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7407 #else
7408   if (CAN_MOVE(element) && !started_moving)
7409 #endif
7410   {
7411     int move_pattern = element_info[element].move_pattern;
7412     int newx, newy;
7413
7414 #if 0
7415 #if DEBUG
7416     if (MovDir[x][y] == MV_NONE)
7417     {
7418       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7419              x, y, element, element_info[element].token_name);
7420       printf("StartMoving(): This should never happen!\n");
7421     }
7422 #endif
7423 #endif
7424
7425     Moving2Blocked(x, y, &newx, &newy);
7426
7427     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7428       return;
7429
7430     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7431         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7432     {
7433       WasJustMoving[x][y] = 0;
7434       CheckCollision[x][y] = 0;
7435
7436       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7437
7438       if (Feld[x][y] != element)        /* element has changed */
7439         return;
7440     }
7441
7442     if (!MovDelay[x][y])        /* start new movement phase */
7443     {
7444       /* all objects that can change their move direction after each step
7445          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7446
7447       if (element != EL_YAMYAM &&
7448           element != EL_DARK_YAMYAM &&
7449           element != EL_PACMAN &&
7450           !(move_pattern & MV_ANY_DIRECTION) &&
7451           move_pattern != MV_TURNING_LEFT &&
7452           move_pattern != MV_TURNING_RIGHT &&
7453           move_pattern != MV_TURNING_LEFT_RIGHT &&
7454           move_pattern != MV_TURNING_RIGHT_LEFT &&
7455           move_pattern != MV_TURNING_RANDOM)
7456       {
7457         TurnRound(x, y);
7458
7459         if (MovDelay[x][y] && (element == EL_BUG ||
7460                                element == EL_SPACESHIP ||
7461                                element == EL_SP_SNIKSNAK ||
7462                                element == EL_SP_ELECTRON ||
7463                                element == EL_MOLE))
7464           DrawLevelField(x, y);
7465       }
7466     }
7467
7468     if (MovDelay[x][y])         /* wait some time before next movement */
7469     {
7470       MovDelay[x][y]--;
7471
7472       if (element == EL_ROBOT ||
7473           element == EL_YAMYAM ||
7474           element == EL_DARK_YAMYAM)
7475       {
7476         DrawLevelElementAnimationIfNeeded(x, y, element);
7477         PlayLevelSoundAction(x, y, ACTION_WAITING);
7478       }
7479       else if (element == EL_SP_ELECTRON)
7480         DrawLevelElementAnimationIfNeeded(x, y, element);
7481       else if (element == EL_DRAGON)
7482       {
7483         int i;
7484         int dir = MovDir[x][y];
7485         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7486         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7487         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7488                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7489                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7490                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7491         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7492
7493         GfxAction[x][y] = ACTION_ATTACKING;
7494
7495         if (IS_PLAYER(x, y))
7496           DrawPlayerField(x, y);
7497         else
7498           DrawLevelField(x, y);
7499
7500         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7501
7502         for (i = 1; i <= 3; i++)
7503         {
7504           int xx = x + i * dx;
7505           int yy = y + i * dy;
7506           int sx = SCREENX(xx);
7507           int sy = SCREENY(yy);
7508           int flame_graphic = graphic + (i - 1);
7509
7510           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7511             break;
7512
7513           if (MovDelay[x][y])
7514           {
7515             int flamed = MovingOrBlocked2Element(xx, yy);
7516
7517             /* !!! */
7518 #if 0
7519             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7520               Bang(xx, yy);
7521             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7522               RemoveMovingField(xx, yy);
7523             else
7524               RemoveField(xx, yy);
7525 #else
7526             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7527               Bang(xx, yy);
7528             else
7529               RemoveMovingField(xx, yy);
7530 #endif
7531
7532             ChangeDelay[xx][yy] = 0;
7533
7534             Feld[xx][yy] = EL_FLAMES;
7535
7536             if (IN_SCR_FIELD(sx, sy))
7537             {
7538               DrawLevelFieldCrumbledSand(xx, yy);
7539               DrawGraphic(sx, sy, flame_graphic, frame);
7540             }
7541           }
7542           else
7543           {
7544             if (Feld[xx][yy] == EL_FLAMES)
7545               Feld[xx][yy] = EL_EMPTY;
7546             DrawLevelField(xx, yy);
7547           }
7548         }
7549       }
7550
7551       if (MovDelay[x][y])       /* element still has to wait some time */
7552       {
7553         PlayLevelSoundAction(x, y, ACTION_WAITING);
7554
7555         return;
7556       }
7557     }
7558
7559     /* now make next step */
7560
7561     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7562
7563     if (DONT_COLLIDE_WITH(element) &&
7564         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7565         !PLAYER_ENEMY_PROTECTED(newx, newy))
7566     {
7567       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7568
7569       return;
7570     }
7571
7572     else if (CAN_MOVE_INTO_ACID(element) &&
7573              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7574              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7575              (MovDir[x][y] == MV_DOWN ||
7576               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7577     {
7578       SplashAcid(newx, newy);
7579       Store[x][y] = EL_ACID;
7580     }
7581     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7582     {
7583       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7584           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7585           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7586           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7587       {
7588         RemoveField(x, y);
7589         DrawLevelField(x, y);
7590
7591         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7592         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7593           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7594
7595         local_player->friends_still_needed--;
7596         if (!local_player->friends_still_needed &&
7597             !local_player->GameOver && AllPlayersGone)
7598           PlayerWins(local_player);
7599
7600         return;
7601       }
7602       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7603       {
7604         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7605           DrawLevelField(newx, newy);
7606         else
7607           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7608       }
7609       else if (!IS_FREE(newx, newy))
7610       {
7611         GfxAction[x][y] = ACTION_WAITING;
7612
7613         if (IS_PLAYER(x, y))
7614           DrawPlayerField(x, y);
7615         else
7616           DrawLevelField(x, y);
7617
7618         return;
7619       }
7620     }
7621     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7622     {
7623       if (IS_FOOD_PIG(Feld[newx][newy]))
7624       {
7625         if (IS_MOVING(newx, newy))
7626           RemoveMovingField(newx, newy);
7627         else
7628         {
7629           Feld[newx][newy] = EL_EMPTY;
7630           DrawLevelField(newx, newy);
7631         }
7632
7633         PlayLevelSound(x, y, SND_PIG_DIGGING);
7634       }
7635       else if (!IS_FREE(newx, newy))
7636       {
7637         if (IS_PLAYER(x, y))
7638           DrawPlayerField(x, y);
7639         else
7640           DrawLevelField(x, y);
7641
7642         return;
7643       }
7644     }
7645     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7646     {
7647       if (Store[x][y] != EL_EMPTY)
7648       {
7649         boolean can_clone = FALSE;
7650         int xx, yy;
7651
7652         /* check if element to clone is still there */
7653         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7654         {
7655           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7656           {
7657             can_clone = TRUE;
7658
7659             break;
7660           }
7661         }
7662
7663         /* cannot clone or target field not free anymore -- do not clone */
7664         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7665           Store[x][y] = EL_EMPTY;
7666       }
7667
7668       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7669       {
7670         if (IS_MV_DIAGONAL(MovDir[x][y]))
7671         {
7672           int diagonal_move_dir = MovDir[x][y];
7673           int stored = Store[x][y];
7674           int change_delay = 8;
7675           int graphic;
7676
7677           /* android is moving diagonally */
7678
7679           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7680
7681           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7682           GfxElement[x][y] = EL_EMC_ANDROID;
7683           GfxAction[x][y] = ACTION_SHRINKING;
7684           GfxDir[x][y] = diagonal_move_dir;
7685           ChangeDelay[x][y] = change_delay;
7686
7687           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7688                                    GfxDir[x][y]);
7689
7690           DrawLevelGraphicAnimation(x, y, graphic);
7691           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7692
7693           if (Feld[newx][newy] == EL_ACID)
7694           {
7695             SplashAcid(newx, newy);
7696
7697             return;
7698           }
7699
7700           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7701
7702           Store[newx][newy] = EL_EMC_ANDROID;
7703           GfxElement[newx][newy] = EL_EMC_ANDROID;
7704           GfxAction[newx][newy] = ACTION_GROWING;
7705           GfxDir[newx][newy] = diagonal_move_dir;
7706           ChangeDelay[newx][newy] = change_delay;
7707
7708           graphic = el_act_dir2img(GfxElement[newx][newy],
7709                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7710
7711           DrawLevelGraphicAnimation(newx, newy, graphic);
7712           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7713
7714           return;
7715         }
7716         else
7717         {
7718           Feld[newx][newy] = EL_EMPTY;
7719           DrawLevelField(newx, newy);
7720
7721           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7722         }
7723       }
7724       else if (!IS_FREE(newx, newy))
7725       {
7726 #if 0
7727         if (IS_PLAYER(x, y))
7728           DrawPlayerField(x, y);
7729         else
7730           DrawLevelField(x, y);
7731 #endif
7732
7733         return;
7734       }
7735     }
7736     else if (IS_CUSTOM_ELEMENT(element) &&
7737              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7738     {
7739       int new_element = Feld[newx][newy];
7740
7741       if (!IS_FREE(newx, newy))
7742       {
7743         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7744                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7745                       ACTION_BREAKING);
7746
7747         /* no element can dig solid indestructible elements */
7748         if (IS_INDESTRUCTIBLE(new_element) &&
7749             !IS_DIGGABLE(new_element) &&
7750             !IS_COLLECTIBLE(new_element))
7751           return;
7752
7753         if (AmoebaNr[newx][newy] &&
7754             (new_element == EL_AMOEBA_FULL ||
7755              new_element == EL_BD_AMOEBA ||
7756              new_element == EL_AMOEBA_GROWING))
7757         {
7758           AmoebaCnt[AmoebaNr[newx][newy]]--;
7759           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7760         }
7761
7762         if (IS_MOVING(newx, newy))
7763           RemoveMovingField(newx, newy);
7764         else
7765         {
7766           RemoveField(newx, newy);
7767           DrawLevelField(newx, newy);
7768         }
7769
7770         /* if digged element was about to explode, prevent the explosion */
7771         ExplodeField[newx][newy] = EX_TYPE_NONE;
7772
7773         PlayLevelSoundAction(x, y, action);
7774       }
7775
7776       Store[newx][newy] = EL_EMPTY;
7777 #if 1
7778       /* this makes it possible to leave the removed element again */
7779       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7780         Store[newx][newy] = new_element;
7781 #else
7782       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7783       {
7784         int move_leave_element = element_info[element].move_leave_element;
7785
7786         /* this makes it possible to leave the removed element again */
7787         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7788                              new_element : move_leave_element);
7789       }
7790 #endif
7791
7792       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7793       {
7794         RunnerVisit[x][y] = FrameCounter;
7795         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7796       }
7797     }
7798     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7799     {
7800       if (!IS_FREE(newx, newy))
7801       {
7802         if (IS_PLAYER(x, y))
7803           DrawPlayerField(x, y);
7804         else
7805           DrawLevelField(x, y);
7806
7807         return;
7808       }
7809       else
7810       {
7811         boolean wanna_flame = !RND(10);
7812         int dx = newx - x, dy = newy - y;
7813         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7814         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7815         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7816                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7817         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7818                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7819
7820         if ((wanna_flame ||
7821              IS_CLASSIC_ENEMY(element1) ||
7822              IS_CLASSIC_ENEMY(element2)) &&
7823             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7824             element1 != EL_FLAMES && element2 != EL_FLAMES)
7825         {
7826           ResetGfxAnimation(x, y);
7827           GfxAction[x][y] = ACTION_ATTACKING;
7828
7829           if (IS_PLAYER(x, y))
7830             DrawPlayerField(x, y);
7831           else
7832             DrawLevelField(x, y);
7833
7834           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7835
7836           MovDelay[x][y] = 50;
7837
7838           /* !!! */
7839 #if 0
7840           RemoveField(newx, newy);
7841 #endif
7842           Feld[newx][newy] = EL_FLAMES;
7843           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7844           {
7845 #if 0
7846             RemoveField(newx1, newy1);
7847 #endif
7848             Feld[newx1][newy1] = EL_FLAMES;
7849           }
7850           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7851           {
7852 #if 0
7853             RemoveField(newx2, newy2);
7854 #endif
7855             Feld[newx2][newy2] = EL_FLAMES;
7856           }
7857
7858           return;
7859         }
7860       }
7861     }
7862     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7863              Feld[newx][newy] == EL_DIAMOND)
7864     {
7865       if (IS_MOVING(newx, newy))
7866         RemoveMovingField(newx, newy);
7867       else
7868       {
7869         Feld[newx][newy] = EL_EMPTY;
7870         DrawLevelField(newx, newy);
7871       }
7872
7873       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7874     }
7875     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7876              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7877     {
7878       if (AmoebaNr[newx][newy])
7879       {
7880         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7881         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7882             Feld[newx][newy] == EL_BD_AMOEBA)
7883           AmoebaCnt[AmoebaNr[newx][newy]]--;
7884       }
7885
7886 #if 0
7887       /* !!! test !!! */
7888       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7889       {
7890         RemoveMovingField(newx, newy);
7891       }
7892 #else
7893       if (IS_MOVING(newx, newy))
7894       {
7895         RemoveMovingField(newx, newy);
7896       }
7897 #endif
7898       else
7899       {
7900         Feld[newx][newy] = EL_EMPTY;
7901         DrawLevelField(newx, newy);
7902       }
7903
7904       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7905     }
7906     else if ((element == EL_PACMAN || element == EL_MOLE)
7907              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7908     {
7909       if (AmoebaNr[newx][newy])
7910       {
7911         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7912         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7913             Feld[newx][newy] == EL_BD_AMOEBA)
7914           AmoebaCnt[AmoebaNr[newx][newy]]--;
7915       }
7916
7917       if (element == EL_MOLE)
7918       {
7919         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7920         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7921
7922         ResetGfxAnimation(x, y);
7923         GfxAction[x][y] = ACTION_DIGGING;
7924         DrawLevelField(x, y);
7925
7926         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7927
7928         return;                         /* wait for shrinking amoeba */
7929       }
7930       else      /* element == EL_PACMAN */
7931       {
7932         Feld[newx][newy] = EL_EMPTY;
7933         DrawLevelField(newx, newy);
7934         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7935       }
7936     }
7937     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7938              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7939               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7940     {
7941       /* wait for shrinking amoeba to completely disappear */
7942       return;
7943     }
7944     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7945     {
7946       /* object was running against a wall */
7947
7948       TurnRound(x, y);
7949
7950 #if 0
7951       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7952       if (move_pattern & MV_ANY_DIRECTION &&
7953           move_pattern == MovDir[x][y])
7954       {
7955         int blocking_element =
7956           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7957
7958         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7959                                  MovDir[x][y]);
7960
7961         element = Feld[x][y];   /* element might have changed */
7962       }
7963 #endif
7964
7965       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7966         DrawLevelElementAnimation(x, y, element);
7967
7968       if (DONT_TOUCH(element))
7969         TestIfBadThingTouchesPlayer(x, y);
7970
7971       return;
7972     }
7973
7974     InitMovingField(x, y, MovDir[x][y]);
7975
7976     PlayLevelSoundAction(x, y, ACTION_MOVING);
7977   }
7978
7979   if (MovDir[x][y])
7980     ContinueMoving(x, y);
7981 }
7982
7983 void ContinueMoving(int x, int y)
7984 {
7985   int element = Feld[x][y];
7986   struct ElementInfo *ei = &element_info[element];
7987   int direction = MovDir[x][y];
7988   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7989   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7990   int newx = x + dx, newy = y + dy;
7991   int stored = Store[x][y];
7992   int stored_new = Store[newx][newy];
7993   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7994   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7995   boolean last_line = (newy == lev_fieldy - 1);
7996
7997   MovPos[x][y] += getElementMoveStepsize(x, y);
7998
7999   if (pushed_by_player) /* special case: moving object pushed by player */
8000     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8001
8002   if (ABS(MovPos[x][y]) < TILEX)
8003   {
8004 #if 0
8005     int ee = Feld[x][y];
8006     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8007     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8008
8009     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8010            x, y, ABS(MovPos[x][y]),
8011            ee, gg, ff,
8012            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8013 #endif
8014
8015     DrawLevelField(x, y);
8016
8017     return;     /* element is still moving */
8018   }
8019
8020   /* element reached destination field */
8021
8022   Feld[x][y] = EL_EMPTY;
8023   Feld[newx][newy] = element;
8024   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8025
8026   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8027   {
8028     element = Feld[newx][newy] = EL_ACID;
8029   }
8030   else if (element == EL_MOLE)
8031   {
8032     Feld[x][y] = EL_SAND;
8033
8034     DrawLevelFieldCrumbledSandNeighbours(x, y);
8035   }
8036   else if (element == EL_QUICKSAND_FILLING)
8037   {
8038     element = Feld[newx][newy] = get_next_element(element);
8039     Store[newx][newy] = Store[x][y];
8040   }
8041   else if (element == EL_QUICKSAND_EMPTYING)
8042   {
8043     Feld[x][y] = get_next_element(element);
8044     element = Feld[newx][newy] = Store[x][y];
8045   }
8046   else if (element == EL_QUICKSAND_FAST_FILLING)
8047   {
8048     element = Feld[newx][newy] = get_next_element(element);
8049     Store[newx][newy] = Store[x][y];
8050   }
8051   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8052   {
8053     Feld[x][y] = get_next_element(element);
8054     element = Feld[newx][newy] = Store[x][y];
8055   }
8056   else if (element == EL_MAGIC_WALL_FILLING)
8057   {
8058     element = Feld[newx][newy] = get_next_element(element);
8059     if (!game.magic_wall_active)
8060       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8061     Store[newx][newy] = Store[x][y];
8062   }
8063   else if (element == EL_MAGIC_WALL_EMPTYING)
8064   {
8065     Feld[x][y] = get_next_element(element);
8066     if (!game.magic_wall_active)
8067       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8068     element = Feld[newx][newy] = Store[x][y];
8069
8070 #if USE_NEW_CUSTOM_VALUE
8071     InitField(newx, newy, FALSE);
8072 #endif
8073   }
8074   else if (element == EL_BD_MAGIC_WALL_FILLING)
8075   {
8076     element = Feld[newx][newy] = get_next_element(element);
8077     if (!game.magic_wall_active)
8078       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8079     Store[newx][newy] = Store[x][y];
8080   }
8081   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8082   {
8083     Feld[x][y] = get_next_element(element);
8084     if (!game.magic_wall_active)
8085       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8086     element = Feld[newx][newy] = Store[x][y];
8087
8088 #if USE_NEW_CUSTOM_VALUE
8089     InitField(newx, newy, FALSE);
8090 #endif
8091   }
8092   else if (element == EL_DC_MAGIC_WALL_FILLING)
8093   {
8094     element = Feld[newx][newy] = get_next_element(element);
8095     if (!game.magic_wall_active)
8096       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8097     Store[newx][newy] = Store[x][y];
8098   }
8099   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8100   {
8101     Feld[x][y] = get_next_element(element);
8102     if (!game.magic_wall_active)
8103       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8104     element = Feld[newx][newy] = Store[x][y];
8105
8106 #if USE_NEW_CUSTOM_VALUE
8107     InitField(newx, newy, FALSE);
8108 #endif
8109   }
8110   else if (element == EL_AMOEBA_DROPPING)
8111   {
8112     Feld[x][y] = get_next_element(element);
8113     element = Feld[newx][newy] = Store[x][y];
8114   }
8115   else if (element == EL_SOKOBAN_OBJECT)
8116   {
8117     if (Back[x][y])
8118       Feld[x][y] = Back[x][y];
8119
8120     if (Back[newx][newy])
8121       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8122
8123     Back[x][y] = Back[newx][newy] = 0;
8124   }
8125
8126   Store[x][y] = EL_EMPTY;
8127   MovPos[x][y] = 0;
8128   MovDir[x][y] = 0;
8129   MovDelay[x][y] = 0;
8130
8131   MovDelay[newx][newy] = 0;
8132
8133   if (CAN_CHANGE_OR_HAS_ACTION(element))
8134   {
8135     /* copy element change control values to new field */
8136     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8137     ChangePage[newx][newy]  = ChangePage[x][y];
8138     ChangeCount[newx][newy] = ChangeCount[x][y];
8139     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8140   }
8141
8142 #if USE_NEW_CUSTOM_VALUE
8143     CustomValue[newx][newy] = CustomValue[x][y];
8144 #endif
8145
8146   ChangeDelay[x][y] = 0;
8147   ChangePage[x][y] = -1;
8148   ChangeCount[x][y] = 0;
8149   ChangeEvent[x][y] = -1;
8150
8151 #if USE_NEW_CUSTOM_VALUE
8152   CustomValue[x][y] = 0;
8153 #endif
8154
8155   /* copy animation control values to new field */
8156   GfxFrame[newx][newy]  = GfxFrame[x][y];
8157   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8158   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8159   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8160
8161   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8162
8163   /* some elements can leave other elements behind after moving */
8164 #if 1
8165   if (ei->move_leave_element != EL_EMPTY &&
8166       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8167       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8168 #else
8169   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8170       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8171       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8172 #endif
8173   {
8174     int move_leave_element = ei->move_leave_element;
8175
8176 #if 1
8177 #if 1
8178     /* this makes it possible to leave the removed element again */
8179     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8180       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8181 #else
8182     /* this makes it possible to leave the removed element again */
8183     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8184       move_leave_element = stored;
8185 #endif
8186 #else
8187     /* this makes it possible to leave the removed element again */
8188     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8189         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8190       move_leave_element = stored;
8191 #endif
8192
8193     Feld[x][y] = move_leave_element;
8194
8195     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8196       MovDir[x][y] = direction;
8197
8198     InitField(x, y, FALSE);
8199
8200     if (GFX_CRUMBLED(Feld[x][y]))
8201       DrawLevelFieldCrumbledSandNeighbours(x, y);
8202
8203     if (ELEM_IS_PLAYER(move_leave_element))
8204       RelocatePlayer(x, y, move_leave_element);
8205   }
8206
8207   /* do this after checking for left-behind element */
8208   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8209
8210   if (!CAN_MOVE(element) ||
8211       (CAN_FALL(element) && direction == MV_DOWN &&
8212        (element == EL_SPRING ||
8213         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8214         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8215     GfxDir[x][y] = MovDir[newx][newy] = 0;
8216
8217   DrawLevelField(x, y);
8218   DrawLevelField(newx, newy);
8219
8220   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8221
8222   /* prevent pushed element from moving on in pushed direction */
8223   if (pushed_by_player && CAN_MOVE(element) &&
8224       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8225       !(element_info[element].move_pattern & direction))
8226     TurnRound(newx, newy);
8227
8228   /* prevent elements on conveyor belt from moving on in last direction */
8229   if (pushed_by_conveyor && CAN_FALL(element) &&
8230       direction & MV_HORIZONTAL)
8231     MovDir[newx][newy] = 0;
8232
8233   if (!pushed_by_player)
8234   {
8235     int nextx = newx + dx, nexty = newy + dy;
8236     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8237
8238     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8239
8240     if (CAN_FALL(element) && direction == MV_DOWN)
8241       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8242
8243     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8244       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8245
8246 #if USE_FIX_IMPACT_COLLISION
8247     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8248       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8249 #endif
8250   }
8251
8252   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8253   {
8254     TestIfBadThingTouchesPlayer(newx, newy);
8255     TestIfBadThingTouchesFriend(newx, newy);
8256
8257     if (!IS_CUSTOM_ELEMENT(element))
8258       TestIfBadThingTouchesOtherBadThing(newx, newy);
8259   }
8260   else if (element == EL_PENGUIN)
8261     TestIfFriendTouchesBadThing(newx, newy);
8262
8263   /* give the player one last chance (one more frame) to move away */
8264   if (CAN_FALL(element) && direction == MV_DOWN &&
8265       (last_line || (!IS_FREE(x, newy + 1) &&
8266                      (!IS_PLAYER(x, newy + 1) ||
8267                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8268     Impact(x, newy);
8269
8270   if (pushed_by_player && !game.use_change_when_pushing_bug)
8271   {
8272     int push_side = MV_DIR_OPPOSITE(direction);
8273     struct PlayerInfo *player = PLAYERINFO(x, y);
8274
8275     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8276                                player->index_bit, push_side);
8277     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8278                                         player->index_bit, push_side);
8279   }
8280
8281   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8282     MovDelay[newx][newy] = 1;
8283
8284   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8285
8286   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8287
8288 #if 0
8289   if (ChangePage[newx][newy] != -1)             /* delayed change */
8290   {
8291     int page = ChangePage[newx][newy];
8292     struct ElementChangeInfo *change = &ei->change_page[page];
8293
8294     ChangePage[newx][newy] = -1;
8295
8296     if (change->can_change)
8297     {
8298       if (ChangeElement(newx, newy, element, page))
8299       {
8300         if (change->post_change_function)
8301           change->post_change_function(newx, newy);
8302       }
8303     }
8304
8305     if (change->has_action)
8306       ExecuteCustomElementAction(newx, newy, element, page);
8307   }
8308 #endif
8309
8310   TestIfElementHitsCustomElement(newx, newy, direction);
8311   TestIfPlayerTouchesCustomElement(newx, newy);
8312   TestIfElementTouchesCustomElement(newx, newy);
8313
8314   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8315       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8316     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8317                              MV_DIR_OPPOSITE(direction));
8318 }
8319
8320 int AmoebeNachbarNr(int ax, int ay)
8321 {
8322   int i;
8323   int element = Feld[ax][ay];
8324   int group_nr = 0;
8325   static int xy[4][2] =
8326   {
8327     { 0, -1 },
8328     { -1, 0 },
8329     { +1, 0 },
8330     { 0, +1 }
8331   };
8332
8333   for (i = 0; i < NUM_DIRECTIONS; i++)
8334   {
8335     int x = ax + xy[i][0];
8336     int y = ay + xy[i][1];
8337
8338     if (!IN_LEV_FIELD(x, y))
8339       continue;
8340
8341     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8342       group_nr = AmoebaNr[x][y];
8343   }
8344
8345   return group_nr;
8346 }
8347
8348 void AmoebenVereinigen(int ax, int ay)
8349 {
8350   int i, x, y, xx, yy;
8351   int new_group_nr = AmoebaNr[ax][ay];
8352   static int xy[4][2] =
8353   {
8354     { 0, -1 },
8355     { -1, 0 },
8356     { +1, 0 },
8357     { 0, +1 }
8358   };
8359
8360   if (new_group_nr == 0)
8361     return;
8362
8363   for (i = 0; i < NUM_DIRECTIONS; i++)
8364   {
8365     x = ax + xy[i][0];
8366     y = ay + xy[i][1];
8367
8368     if (!IN_LEV_FIELD(x, y))
8369       continue;
8370
8371     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8372          Feld[x][y] == EL_BD_AMOEBA ||
8373          Feld[x][y] == EL_AMOEBA_DEAD) &&
8374         AmoebaNr[x][y] != new_group_nr)
8375     {
8376       int old_group_nr = AmoebaNr[x][y];
8377
8378       if (old_group_nr == 0)
8379         return;
8380
8381       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8382       AmoebaCnt[old_group_nr] = 0;
8383       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8384       AmoebaCnt2[old_group_nr] = 0;
8385
8386       SCAN_PLAYFIELD(xx, yy)
8387       {
8388         if (AmoebaNr[xx][yy] == old_group_nr)
8389           AmoebaNr[xx][yy] = new_group_nr;
8390       }
8391     }
8392   }
8393 }
8394
8395 void AmoebeUmwandeln(int ax, int ay)
8396 {
8397   int i, x, y;
8398
8399   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8400   {
8401     int group_nr = AmoebaNr[ax][ay];
8402
8403 #ifdef DEBUG
8404     if (group_nr == 0)
8405     {
8406       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8407       printf("AmoebeUmwandeln(): This should never happen!\n");
8408       return;
8409     }
8410 #endif
8411
8412     SCAN_PLAYFIELD(x, y)
8413     {
8414       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8415       {
8416         AmoebaNr[x][y] = 0;
8417         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8418       }
8419     }
8420
8421     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8422                             SND_AMOEBA_TURNING_TO_GEM :
8423                             SND_AMOEBA_TURNING_TO_ROCK));
8424     Bang(ax, ay);
8425   }
8426   else
8427   {
8428     static int xy[4][2] =
8429     {
8430       { 0, -1 },
8431       { -1, 0 },
8432       { +1, 0 },
8433       { 0, +1 }
8434     };
8435
8436     for (i = 0; i < NUM_DIRECTIONS; i++)
8437     {
8438       x = ax + xy[i][0];
8439       y = ay + xy[i][1];
8440
8441       if (!IN_LEV_FIELD(x, y))
8442         continue;
8443
8444       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8445       {
8446         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8447                               SND_AMOEBA_TURNING_TO_GEM :
8448                               SND_AMOEBA_TURNING_TO_ROCK));
8449         Bang(x, y);
8450       }
8451     }
8452   }
8453 }
8454
8455 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8456 {
8457   int x, y;
8458   int group_nr = AmoebaNr[ax][ay];
8459   boolean done = FALSE;
8460
8461 #ifdef DEBUG
8462   if (group_nr == 0)
8463   {
8464     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8465     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8466     return;
8467   }
8468 #endif
8469
8470   SCAN_PLAYFIELD(x, y)
8471   {
8472     if (AmoebaNr[x][y] == group_nr &&
8473         (Feld[x][y] == EL_AMOEBA_DEAD ||
8474          Feld[x][y] == EL_BD_AMOEBA ||
8475          Feld[x][y] == EL_AMOEBA_GROWING))
8476     {
8477       AmoebaNr[x][y] = 0;
8478       Feld[x][y] = new_element;
8479       InitField(x, y, FALSE);
8480       DrawLevelField(x, y);
8481       done = TRUE;
8482     }
8483   }
8484
8485   if (done)
8486     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8487                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8488                             SND_BD_AMOEBA_TURNING_TO_GEM));
8489 }
8490
8491 void AmoebeWaechst(int x, int y)
8492 {
8493   static unsigned long sound_delay = 0;
8494   static unsigned long sound_delay_value = 0;
8495
8496   if (!MovDelay[x][y])          /* start new growing cycle */
8497   {
8498     MovDelay[x][y] = 7;
8499
8500     if (DelayReached(&sound_delay, sound_delay_value))
8501     {
8502       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8503       sound_delay_value = 30;
8504     }
8505   }
8506
8507   if (MovDelay[x][y])           /* wait some time before growing bigger */
8508   {
8509     MovDelay[x][y]--;
8510     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8511     {
8512       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8513                                            6 - MovDelay[x][y]);
8514
8515       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8516     }
8517
8518     if (!MovDelay[x][y])
8519     {
8520       Feld[x][y] = Store[x][y];
8521       Store[x][y] = 0;
8522       DrawLevelField(x, y);
8523     }
8524   }
8525 }
8526
8527 void AmoebaDisappearing(int x, int y)
8528 {
8529   static unsigned long sound_delay = 0;
8530   static unsigned long sound_delay_value = 0;
8531
8532   if (!MovDelay[x][y])          /* start new shrinking cycle */
8533   {
8534     MovDelay[x][y] = 7;
8535
8536     if (DelayReached(&sound_delay, sound_delay_value))
8537       sound_delay_value = 30;
8538   }
8539
8540   if (MovDelay[x][y])           /* wait some time before shrinking */
8541   {
8542     MovDelay[x][y]--;
8543     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8544     {
8545       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8546                                            6 - MovDelay[x][y]);
8547
8548       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8549     }
8550
8551     if (!MovDelay[x][y])
8552     {
8553       Feld[x][y] = EL_EMPTY;
8554       DrawLevelField(x, y);
8555
8556       /* don't let mole enter this field in this cycle;
8557          (give priority to objects falling to this field from above) */
8558       Stop[x][y] = TRUE;
8559     }
8560   }
8561 }
8562
8563 void AmoebeAbleger(int ax, int ay)
8564 {
8565   int i;
8566   int element = Feld[ax][ay];
8567   int graphic = el2img(element);
8568   int newax = ax, neway = ay;
8569   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8570   static int xy[4][2] =
8571   {
8572     { 0, -1 },
8573     { -1, 0 },
8574     { +1, 0 },
8575     { 0, +1 }
8576   };
8577
8578   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8579   {
8580     Feld[ax][ay] = EL_AMOEBA_DEAD;
8581     DrawLevelField(ax, ay);
8582     return;
8583   }
8584
8585   if (IS_ANIMATED(graphic))
8586     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8587
8588   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8589     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8590
8591   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8592   {
8593     MovDelay[ax][ay]--;
8594     if (MovDelay[ax][ay])
8595       return;
8596   }
8597
8598   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8599   {
8600     int start = RND(4);
8601     int x = ax + xy[start][0];
8602     int y = ay + xy[start][1];
8603
8604     if (!IN_LEV_FIELD(x, y))
8605       return;
8606
8607     if (IS_FREE(x, y) ||
8608         CAN_GROW_INTO(Feld[x][y]) ||
8609         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8610         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8611     {
8612       newax = x;
8613       neway = y;
8614     }
8615
8616     if (newax == ax && neway == ay)
8617       return;
8618   }
8619   else                          /* normal or "filled" (BD style) amoeba */
8620   {
8621     int start = RND(4);
8622     boolean waiting_for_player = FALSE;
8623
8624     for (i = 0; i < NUM_DIRECTIONS; i++)
8625     {
8626       int j = (start + i) % 4;
8627       int x = ax + xy[j][0];
8628       int y = ay + xy[j][1];
8629
8630       if (!IN_LEV_FIELD(x, y))
8631         continue;
8632
8633       if (IS_FREE(x, y) ||
8634           CAN_GROW_INTO(Feld[x][y]) ||
8635           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8636           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8637       {
8638         newax = x;
8639         neway = y;
8640         break;
8641       }
8642       else if (IS_PLAYER(x, y))
8643         waiting_for_player = TRUE;
8644     }
8645
8646     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8647     {
8648       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8649       {
8650         Feld[ax][ay] = EL_AMOEBA_DEAD;
8651         DrawLevelField(ax, ay);
8652         AmoebaCnt[AmoebaNr[ax][ay]]--;
8653
8654         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8655         {
8656           if (element == EL_AMOEBA_FULL)
8657             AmoebeUmwandeln(ax, ay);
8658           else if (element == EL_BD_AMOEBA)
8659             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8660         }
8661       }
8662       return;
8663     }
8664     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8665     {
8666       /* amoeba gets larger by growing in some direction */
8667
8668       int new_group_nr = AmoebaNr[ax][ay];
8669
8670 #ifdef DEBUG
8671   if (new_group_nr == 0)
8672   {
8673     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8674     printf("AmoebeAbleger(): This should never happen!\n");
8675     return;
8676   }
8677 #endif
8678
8679       AmoebaNr[newax][neway] = new_group_nr;
8680       AmoebaCnt[new_group_nr]++;
8681       AmoebaCnt2[new_group_nr]++;
8682
8683       /* if amoeba touches other amoeba(s) after growing, unify them */
8684       AmoebenVereinigen(newax, neway);
8685
8686       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8687       {
8688         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8689         return;
8690       }
8691     }
8692   }
8693
8694   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8695       (neway == lev_fieldy - 1 && newax != ax))
8696   {
8697     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8698     Store[newax][neway] = element;
8699   }
8700   else if (neway == ay || element == EL_EMC_DRIPPER)
8701   {
8702     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8703
8704     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8705   }
8706   else
8707   {
8708     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8709     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8710     Store[ax][ay] = EL_AMOEBA_DROP;
8711     ContinueMoving(ax, ay);
8712     return;
8713   }
8714
8715   DrawLevelField(newax, neway);
8716 }
8717
8718 void Life(int ax, int ay)
8719 {
8720   int x1, y1, x2, y2;
8721   int life_time = 40;
8722   int element = Feld[ax][ay];
8723   int graphic = el2img(element);
8724   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8725                          level.biomaze);
8726   boolean changed = FALSE;
8727
8728   if (IS_ANIMATED(graphic))
8729     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8730
8731   if (Stop[ax][ay])
8732     return;
8733
8734   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8735     MovDelay[ax][ay] = life_time;
8736
8737   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8738   {
8739     MovDelay[ax][ay]--;
8740     if (MovDelay[ax][ay])
8741       return;
8742   }
8743
8744   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8745   {
8746     int xx = ax+x1, yy = ay+y1;
8747     int nachbarn = 0;
8748
8749     if (!IN_LEV_FIELD(xx, yy))
8750       continue;
8751
8752     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8753     {
8754       int x = xx+x2, y = yy+y2;
8755
8756       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8757         continue;
8758
8759       if (((Feld[x][y] == element ||
8760             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8761            !Stop[x][y]) ||
8762           (IS_FREE(x, y) && Stop[x][y]))
8763         nachbarn++;
8764     }
8765
8766     if (xx == ax && yy == ay)           /* field in the middle */
8767     {
8768       if (nachbarn < life_parameter[0] ||
8769           nachbarn > life_parameter[1])
8770       {
8771         Feld[xx][yy] = EL_EMPTY;
8772         if (!Stop[xx][yy])
8773           DrawLevelField(xx, yy);
8774         Stop[xx][yy] = TRUE;
8775         changed = TRUE;
8776       }
8777     }
8778     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8779     {                                   /* free border field */
8780       if (nachbarn >= life_parameter[2] &&
8781           nachbarn <= life_parameter[3])
8782       {
8783         Feld[xx][yy] = element;
8784         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8785         if (!Stop[xx][yy])
8786           DrawLevelField(xx, yy);
8787         Stop[xx][yy] = TRUE;
8788         changed = TRUE;
8789       }
8790     }
8791   }
8792
8793   if (changed)
8794     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8795                    SND_GAME_OF_LIFE_GROWING);
8796 }
8797
8798 static void InitRobotWheel(int x, int y)
8799 {
8800   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8801 }
8802
8803 static void RunRobotWheel(int x, int y)
8804 {
8805   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8806 }
8807
8808 static void StopRobotWheel(int x, int y)
8809 {
8810   if (ZX == x && ZY == y)
8811   {
8812     ZX = ZY = -1;
8813
8814     game.robot_wheel_active = FALSE;
8815   }
8816 }
8817
8818 static void InitTimegateWheel(int x, int y)
8819 {
8820   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8821 }
8822
8823 static void RunTimegateWheel(int x, int y)
8824 {
8825   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8826 }
8827
8828 static void InitMagicBallDelay(int x, int y)
8829 {
8830 #if 1
8831   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8832 #else
8833   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8834 #endif
8835 }
8836
8837 static void ActivateMagicBall(int bx, int by)
8838 {
8839   int x, y;
8840
8841   if (level.ball_random)
8842   {
8843     int pos_border = RND(8);    /* select one of the eight border elements */
8844     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8845     int xx = pos_content % 3;
8846     int yy = pos_content / 3;
8847
8848     x = bx - 1 + xx;
8849     y = by - 1 + yy;
8850
8851     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8852       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8853   }
8854   else
8855   {
8856     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8857     {
8858       int xx = x - bx + 1;
8859       int yy = y - by + 1;
8860
8861       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8862         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8863     }
8864   }
8865
8866   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8867 }
8868
8869 void CheckExit(int x, int y)
8870 {
8871   if (local_player->gems_still_needed > 0 ||
8872       local_player->sokobanfields_still_needed > 0 ||
8873       local_player->lights_still_needed > 0)
8874   {
8875     int element = Feld[x][y];
8876     int graphic = el2img(element);
8877
8878     if (IS_ANIMATED(graphic))
8879       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8880
8881     return;
8882   }
8883
8884   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8885     return;
8886
8887   Feld[x][y] = EL_EXIT_OPENING;
8888
8889   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8890 }
8891
8892 void CheckExitEM(int x, int y)
8893 {
8894   if (local_player->gems_still_needed > 0 ||
8895       local_player->sokobanfields_still_needed > 0 ||
8896       local_player->lights_still_needed > 0)
8897   {
8898     int element = Feld[x][y];
8899     int graphic = el2img(element);
8900
8901     if (IS_ANIMATED(graphic))
8902       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8903
8904     return;
8905   }
8906
8907   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8908     return;
8909
8910   Feld[x][y] = EL_EM_EXIT_OPENING;
8911
8912   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8913 }
8914
8915 void CheckExitSteel(int x, int y)
8916 {
8917   if (local_player->gems_still_needed > 0 ||
8918       local_player->sokobanfields_still_needed > 0 ||
8919       local_player->lights_still_needed > 0)
8920   {
8921     int element = Feld[x][y];
8922     int graphic = el2img(element);
8923
8924     if (IS_ANIMATED(graphic))
8925       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8926
8927     return;
8928   }
8929
8930   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8931     return;
8932
8933   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8934
8935   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8936 }
8937
8938 void CheckExitSteelEM(int x, int y)
8939 {
8940   if (local_player->gems_still_needed > 0 ||
8941       local_player->sokobanfields_still_needed > 0 ||
8942       local_player->lights_still_needed > 0)
8943   {
8944     int element = Feld[x][y];
8945     int graphic = el2img(element);
8946
8947     if (IS_ANIMATED(graphic))
8948       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8949
8950     return;
8951   }
8952
8953   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8954     return;
8955
8956   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8957
8958   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8959 }
8960
8961 void CheckExitSP(int x, int y)
8962 {
8963   if (local_player->gems_still_needed > 0)
8964   {
8965     int element = Feld[x][y];
8966     int graphic = el2img(element);
8967
8968     if (IS_ANIMATED(graphic))
8969       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8970
8971     return;
8972   }
8973
8974   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8975     return;
8976
8977   Feld[x][y] = EL_SP_EXIT_OPENING;
8978
8979   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8980 }
8981
8982 static void CloseAllOpenTimegates()
8983 {
8984   int x, y;
8985
8986   SCAN_PLAYFIELD(x, y)
8987   {
8988     int element = Feld[x][y];
8989
8990     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8991     {
8992       Feld[x][y] = EL_TIMEGATE_CLOSING;
8993
8994       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8995     }
8996   }
8997 }
8998
8999 void DrawTwinkleOnField(int x, int y)
9000 {
9001   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9002     return;
9003
9004   if (Feld[x][y] == EL_BD_DIAMOND)
9005     return;
9006
9007   if (MovDelay[x][y] == 0)      /* next animation frame */
9008     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9009
9010   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9011   {
9012     MovDelay[x][y]--;
9013
9014     if (setup.direct_draw && MovDelay[x][y])
9015       SetDrawtoField(DRAW_BUFFERED);
9016
9017     DrawLevelElementAnimation(x, y, Feld[x][y]);
9018
9019     if (MovDelay[x][y] != 0)
9020     {
9021       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9022                                            10 - MovDelay[x][y]);
9023
9024       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9025
9026       if (setup.direct_draw)
9027       {
9028         int dest_x, dest_y;
9029
9030         dest_x = FX + SCREENX(x) * TILEX;
9031         dest_y = FY + SCREENY(y) * TILEY;
9032
9033         BlitBitmap(drawto_field, window,
9034                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9035         SetDrawtoField(DRAW_DIRECT);
9036       }
9037     }
9038   }
9039 }
9040
9041 void MauerWaechst(int x, int y)
9042 {
9043   int delay = 6;
9044
9045   if (!MovDelay[x][y])          /* next animation frame */
9046     MovDelay[x][y] = 3 * delay;
9047
9048   if (MovDelay[x][y])           /* wait some time before next frame */
9049   {
9050     MovDelay[x][y]--;
9051
9052     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9053     {
9054       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9055       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9056
9057       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9058     }
9059
9060     if (!MovDelay[x][y])
9061     {
9062       if (MovDir[x][y] == MV_LEFT)
9063       {
9064         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9065           DrawLevelField(x - 1, y);
9066       }
9067       else if (MovDir[x][y] == MV_RIGHT)
9068       {
9069         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9070           DrawLevelField(x + 1, y);
9071       }
9072       else if (MovDir[x][y] == MV_UP)
9073       {
9074         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9075           DrawLevelField(x, y - 1);
9076       }
9077       else
9078       {
9079         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9080           DrawLevelField(x, y + 1);
9081       }
9082
9083       Feld[x][y] = Store[x][y];
9084       Store[x][y] = 0;
9085       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9086       DrawLevelField(x, y);
9087     }
9088   }
9089 }
9090
9091 void MauerAbleger(int ax, int ay)
9092 {
9093   int element = Feld[ax][ay];
9094   int graphic = el2img(element);
9095   boolean oben_frei = FALSE, unten_frei = FALSE;
9096   boolean links_frei = FALSE, rechts_frei = FALSE;
9097   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9098   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9099   boolean new_wall = FALSE;
9100
9101   if (IS_ANIMATED(graphic))
9102     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9103
9104   if (!MovDelay[ax][ay])        /* start building new wall */
9105     MovDelay[ax][ay] = 6;
9106
9107   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9108   {
9109     MovDelay[ax][ay]--;
9110     if (MovDelay[ax][ay])
9111       return;
9112   }
9113
9114   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9115     oben_frei = TRUE;
9116   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9117     unten_frei = TRUE;
9118   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9119     links_frei = TRUE;
9120   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9121     rechts_frei = TRUE;
9122
9123   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9124       element == EL_EXPANDABLE_WALL_ANY)
9125   {
9126     if (oben_frei)
9127     {
9128       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9129       Store[ax][ay-1] = element;
9130       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9131       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9132         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9133                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9134       new_wall = TRUE;
9135     }
9136     if (unten_frei)
9137     {
9138       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9139       Store[ax][ay+1] = element;
9140       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9141       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9142         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9143                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9144       new_wall = TRUE;
9145     }
9146   }
9147
9148   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9149       element == EL_EXPANDABLE_WALL_ANY ||
9150       element == EL_EXPANDABLE_WALL ||
9151       element == EL_BD_EXPANDABLE_WALL)
9152   {
9153     if (links_frei)
9154     {
9155       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9156       Store[ax-1][ay] = element;
9157       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9158       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9159         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9160                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9161       new_wall = TRUE;
9162     }
9163
9164     if (rechts_frei)
9165     {
9166       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9167       Store[ax+1][ay] = element;
9168       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9169       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9170         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9171                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9172       new_wall = TRUE;
9173     }
9174   }
9175
9176   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9177     DrawLevelField(ax, ay);
9178
9179   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9180     oben_massiv = TRUE;
9181   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9182     unten_massiv = TRUE;
9183   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9184     links_massiv = TRUE;
9185   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9186     rechts_massiv = TRUE;
9187
9188   if (((oben_massiv && unten_massiv) ||
9189        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9190        element == EL_EXPANDABLE_WALL) &&
9191       ((links_massiv && rechts_massiv) ||
9192        element == EL_EXPANDABLE_WALL_VERTICAL))
9193     Feld[ax][ay] = EL_WALL;
9194
9195   if (new_wall)
9196     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9197 }
9198
9199 void MauerAblegerStahl(int ax, int ay)
9200 {
9201   int element = Feld[ax][ay];
9202   int graphic = el2img(element);
9203   boolean oben_frei = FALSE, unten_frei = FALSE;
9204   boolean links_frei = FALSE, rechts_frei = FALSE;
9205   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9206   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9207   boolean new_wall = FALSE;
9208
9209   if (IS_ANIMATED(graphic))
9210     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9211
9212   if (!MovDelay[ax][ay])        /* start building new wall */
9213     MovDelay[ax][ay] = 6;
9214
9215   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9216   {
9217     MovDelay[ax][ay]--;
9218     if (MovDelay[ax][ay])
9219       return;
9220   }
9221
9222   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9223     oben_frei = TRUE;
9224   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9225     unten_frei = TRUE;
9226   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9227     links_frei = TRUE;
9228   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9229     rechts_frei = TRUE;
9230
9231   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9232       element == EL_EXPANDABLE_STEELWALL_ANY)
9233   {
9234     if (oben_frei)
9235     {
9236       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9237       Store[ax][ay-1] = element;
9238       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9239       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9240         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9241                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9242       new_wall = TRUE;
9243     }
9244     if (unten_frei)
9245     {
9246       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9247       Store[ax][ay+1] = element;
9248       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9249       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9250         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9251                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9252       new_wall = TRUE;
9253     }
9254   }
9255
9256   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9257       element == EL_EXPANDABLE_STEELWALL_ANY)
9258   {
9259     if (links_frei)
9260     {
9261       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9262       Store[ax-1][ay] = element;
9263       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9264       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9265         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9266                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9267       new_wall = TRUE;
9268     }
9269
9270     if (rechts_frei)
9271     {
9272       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9273       Store[ax+1][ay] = element;
9274       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9275       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9276         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9277                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9278       new_wall = TRUE;
9279     }
9280   }
9281
9282   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9283     oben_massiv = TRUE;
9284   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9285     unten_massiv = TRUE;
9286   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9287     links_massiv = TRUE;
9288   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9289     rechts_massiv = TRUE;
9290
9291   if (((oben_massiv && unten_massiv) ||
9292        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9293       ((links_massiv && rechts_massiv) ||
9294        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9295     Feld[ax][ay] = EL_WALL;
9296
9297   if (new_wall)
9298     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9299 }
9300
9301 void CheckForDragon(int x, int y)
9302 {
9303   int i, j;
9304   boolean dragon_found = FALSE;
9305   static int xy[4][2] =
9306   {
9307     { 0, -1 },
9308     { -1, 0 },
9309     { +1, 0 },
9310     { 0, +1 }
9311   };
9312
9313   for (i = 0; i < NUM_DIRECTIONS; i++)
9314   {
9315     for (j = 0; j < 4; j++)
9316     {
9317       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9318
9319       if (IN_LEV_FIELD(xx, yy) &&
9320           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9321       {
9322         if (Feld[xx][yy] == EL_DRAGON)
9323           dragon_found = TRUE;
9324       }
9325       else
9326         break;
9327     }
9328   }
9329
9330   if (!dragon_found)
9331   {
9332     for (i = 0; i < NUM_DIRECTIONS; i++)
9333     {
9334       for (j = 0; j < 3; j++)
9335       {
9336         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9337   
9338         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9339         {
9340           Feld[xx][yy] = EL_EMPTY;
9341           DrawLevelField(xx, yy);
9342         }
9343         else
9344           break;
9345       }
9346     }
9347   }
9348 }
9349
9350 static void InitBuggyBase(int x, int y)
9351 {
9352   int element = Feld[x][y];
9353   int activating_delay = FRAMES_PER_SECOND / 4;
9354
9355   ChangeDelay[x][y] =
9356     (element == EL_SP_BUGGY_BASE ?
9357      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9358      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9359      activating_delay :
9360      element == EL_SP_BUGGY_BASE_ACTIVE ?
9361      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9362 }
9363
9364 static void WarnBuggyBase(int x, int y)
9365 {
9366   int i;
9367   static int xy[4][2] =
9368   {
9369     { 0, -1 },
9370     { -1, 0 },
9371     { +1, 0 },
9372     { 0, +1 }
9373   };
9374
9375   for (i = 0; i < NUM_DIRECTIONS; i++)
9376   {
9377     int xx = x + xy[i][0];
9378     int yy = y + xy[i][1];
9379
9380     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9381     {
9382       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9383
9384       break;
9385     }
9386   }
9387 }
9388
9389 static void InitTrap(int x, int y)
9390 {
9391   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9392 }
9393
9394 static void ActivateTrap(int x, int y)
9395 {
9396   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9397 }
9398
9399 static void ChangeActiveTrap(int x, int y)
9400 {
9401   int graphic = IMG_TRAP_ACTIVE;
9402
9403   /* if new animation frame was drawn, correct crumbled sand border */
9404   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9405     DrawLevelFieldCrumbledSand(x, y);
9406 }
9407
9408 static int getSpecialActionElement(int element, int number, int base_element)
9409 {
9410   return (element != EL_EMPTY ? element :
9411           number != -1 ? base_element + number - 1 :
9412           EL_EMPTY);
9413 }
9414
9415 static int getModifiedActionNumber(int value_old, int operator, int operand,
9416                                    int value_min, int value_max)
9417 {
9418   int value_new = (operator == CA_MODE_SET      ? operand :
9419                    operator == CA_MODE_ADD      ? value_old + operand :
9420                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9421                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9422                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9423                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9424                    value_old);
9425
9426   return (value_new < value_min ? value_min :
9427           value_new > value_max ? value_max :
9428           value_new);
9429 }
9430
9431 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9432 {
9433   struct ElementInfo *ei = &element_info[element];
9434   struct ElementChangeInfo *change = &ei->change_page[page];
9435   int target_element = change->target_element;
9436   int action_type = change->action_type;
9437   int action_mode = change->action_mode;
9438   int action_arg = change->action_arg;
9439   int i;
9440
9441   if (!change->has_action)
9442     return;
9443
9444   /* ---------- determine action paramater values -------------------------- */
9445
9446   int level_time_value =
9447     (level.time > 0 ? TimeLeft :
9448      TimePlayed);
9449
9450   int action_arg_element =
9451     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9452      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9453      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9454      EL_EMPTY);
9455
9456   int action_arg_direction =
9457     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9458      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9459      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9460      change->actual_trigger_side :
9461      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9462      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9463      MV_NONE);
9464
9465   int action_arg_number_min =
9466     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9467      CA_ARG_MIN);
9468
9469   int action_arg_number_max =
9470     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9471      action_type == CA_SET_LEVEL_GEMS ? 999 :
9472      action_type == CA_SET_LEVEL_TIME ? 9999 :
9473      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9474      action_type == CA_SET_CE_VALUE ? 9999 :
9475      action_type == CA_SET_CE_SCORE ? 9999 :
9476      CA_ARG_MAX);
9477
9478   int action_arg_number_reset =
9479     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9480      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9481      action_type == CA_SET_LEVEL_TIME ? level.time :
9482      action_type == CA_SET_LEVEL_SCORE ? 0 :
9483 #if USE_NEW_CUSTOM_VALUE
9484      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9485 #else
9486      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9487 #endif
9488      action_type == CA_SET_CE_SCORE ? 0 :
9489      0);
9490
9491   int action_arg_number =
9492     (action_arg <= CA_ARG_MAX ? action_arg :
9493      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9494      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9495      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9496      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9497      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9498      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9499 #if USE_NEW_CUSTOM_VALUE
9500      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9501 #else
9502      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9503 #endif
9504      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9505      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9506      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9507      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9508      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9509      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9510      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9511      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9512      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9513      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9514      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9515      -1);
9516
9517   int action_arg_number_old =
9518     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9519      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9520      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9521      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9522      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9523      0);
9524
9525   int action_arg_number_new =
9526     getModifiedActionNumber(action_arg_number_old,
9527                             action_mode, action_arg_number,
9528                             action_arg_number_min, action_arg_number_max);
9529
9530   int trigger_player_bits =
9531     (change->actual_trigger_player >= EL_PLAYER_1 &&
9532      change->actual_trigger_player <= EL_PLAYER_4 ?
9533      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9534      PLAYER_BITS_ANY);
9535
9536   int action_arg_player_bits =
9537     (action_arg >= CA_ARG_PLAYER_1 &&
9538      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9539      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9540      PLAYER_BITS_ANY);
9541
9542   /* ---------- execute action  -------------------------------------------- */
9543
9544   switch (action_type)
9545   {
9546     case CA_NO_ACTION:
9547     {
9548       return;
9549     }
9550
9551     /* ---------- level actions  ------------------------------------------- */
9552
9553     case CA_RESTART_LEVEL:
9554     {
9555       game.restart_level = TRUE;
9556
9557       break;
9558     }
9559
9560     case CA_SHOW_ENVELOPE:
9561     {
9562       int element = getSpecialActionElement(action_arg_element,
9563                                             action_arg_number, EL_ENVELOPE_1);
9564
9565       if (IS_ENVELOPE(element))
9566         local_player->show_envelope = element;
9567
9568       break;
9569     }
9570
9571     case CA_SET_LEVEL_TIME:
9572     {
9573       if (level.time > 0)       /* only modify limited time value */
9574       {
9575         TimeLeft = action_arg_number_new;
9576
9577 #if 1
9578         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9579
9580         DisplayGameControlValues();
9581 #else
9582         DrawGameValue_Time(TimeLeft);
9583 #endif
9584
9585         if (!TimeLeft && setup.time_limit)
9586           for (i = 0; i < MAX_PLAYERS; i++)
9587             KillPlayer(&stored_player[i]);
9588       }
9589
9590       break;
9591     }
9592
9593     case CA_SET_LEVEL_SCORE:
9594     {
9595       local_player->score = action_arg_number_new;
9596
9597 #if 1
9598       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9599
9600       DisplayGameControlValues();
9601 #else
9602       DrawGameValue_Score(local_player->score);
9603 #endif
9604
9605       break;
9606     }
9607
9608     case CA_SET_LEVEL_GEMS:
9609     {
9610       local_player->gems_still_needed = action_arg_number_new;
9611
9612 #if 1
9613       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9614
9615       DisplayGameControlValues();
9616 #else
9617       DrawGameValue_Emeralds(local_player->gems_still_needed);
9618 #endif
9619
9620       break;
9621     }
9622
9623 #if !USE_PLAYER_GRAVITY
9624     case CA_SET_LEVEL_GRAVITY:
9625     {
9626       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9627                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9628                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9629                       game.gravity);
9630       break;
9631     }
9632 #endif
9633
9634     case CA_SET_LEVEL_WIND:
9635     {
9636       game.wind_direction = action_arg_direction;
9637
9638       break;
9639     }
9640
9641     /* ---------- player actions  ------------------------------------------ */
9642
9643     case CA_MOVE_PLAYER:
9644     {
9645       /* automatically move to the next field in specified direction */
9646       for (i = 0; i < MAX_PLAYERS; i++)
9647         if (trigger_player_bits & (1 << i))
9648           stored_player[i].programmed_action = action_arg_direction;
9649
9650       break;
9651     }
9652
9653     case CA_EXIT_PLAYER:
9654     {
9655       for (i = 0; i < MAX_PLAYERS; i++)
9656         if (action_arg_player_bits & (1 << i))
9657           PlayerWins(&stored_player[i]);
9658
9659       break;
9660     }
9661
9662     case CA_KILL_PLAYER:
9663     {
9664       for (i = 0; i < MAX_PLAYERS; i++)
9665         if (action_arg_player_bits & (1 << i))
9666           KillPlayer(&stored_player[i]);
9667
9668       break;
9669     }
9670
9671     case CA_SET_PLAYER_KEYS:
9672     {
9673       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9674       int element = getSpecialActionElement(action_arg_element,
9675                                             action_arg_number, EL_KEY_1);
9676
9677       if (IS_KEY(element))
9678       {
9679         for (i = 0; i < MAX_PLAYERS; i++)
9680         {
9681           if (trigger_player_bits & (1 << i))
9682           {
9683             stored_player[i].key[KEY_NR(element)] = key_state;
9684
9685             DrawGameDoorValues();
9686           }
9687         }
9688       }
9689
9690       break;
9691     }
9692
9693     case CA_SET_PLAYER_SPEED:
9694     {
9695       for (i = 0; i < MAX_PLAYERS; i++)
9696       {
9697         if (trigger_player_bits & (1 << i))
9698         {
9699           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9700
9701           if (action_arg == CA_ARG_SPEED_FASTER &&
9702               stored_player[i].cannot_move)
9703           {
9704             action_arg_number = STEPSIZE_VERY_SLOW;
9705           }
9706           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9707                    action_arg == CA_ARG_SPEED_FASTER)
9708           {
9709             action_arg_number = 2;
9710             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9711                            CA_MODE_MULTIPLY);
9712           }
9713           else if (action_arg == CA_ARG_NUMBER_RESET)
9714           {
9715             action_arg_number = level.initial_player_stepsize[i];
9716           }
9717
9718           move_stepsize =
9719             getModifiedActionNumber(move_stepsize,
9720                                     action_mode,
9721                                     action_arg_number,
9722                                     action_arg_number_min,
9723                                     action_arg_number_max);
9724
9725           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9726         }
9727       }
9728
9729       break;
9730     }
9731
9732     case CA_SET_PLAYER_SHIELD:
9733     {
9734       for (i = 0; i < MAX_PLAYERS; i++)
9735       {
9736         if (trigger_player_bits & (1 << i))
9737         {
9738           if (action_arg == CA_ARG_SHIELD_OFF)
9739           {
9740             stored_player[i].shield_normal_time_left = 0;
9741             stored_player[i].shield_deadly_time_left = 0;
9742           }
9743           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9744           {
9745             stored_player[i].shield_normal_time_left = 999999;
9746           }
9747           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9748           {
9749             stored_player[i].shield_normal_time_left = 999999;
9750             stored_player[i].shield_deadly_time_left = 999999;
9751           }
9752         }
9753       }
9754
9755       break;
9756     }
9757
9758 #if USE_PLAYER_GRAVITY
9759     case CA_SET_PLAYER_GRAVITY:
9760     {
9761       for (i = 0; i < MAX_PLAYERS; i++)
9762       {
9763         if (trigger_player_bits & (1 << i))
9764         {
9765           stored_player[i].gravity =
9766             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9767              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9768              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9769              stored_player[i].gravity);
9770         }
9771       }
9772
9773       break;
9774     }
9775 #endif
9776
9777     case CA_SET_PLAYER_ARTWORK:
9778     {
9779       for (i = 0; i < MAX_PLAYERS; i++)
9780       {
9781         if (trigger_player_bits & (1 << i))
9782         {
9783           int artwork_element = action_arg_element;
9784
9785           if (action_arg == CA_ARG_ELEMENT_RESET)
9786             artwork_element =
9787               (level.use_artwork_element[i] ? level.artwork_element[i] :
9788                stored_player[i].element_nr);
9789
9790 #if USE_GFX_RESET_PLAYER_ARTWORK
9791           if (stored_player[i].artwork_element != artwork_element)
9792             stored_player[i].Frame = 0;
9793 #endif
9794
9795           stored_player[i].artwork_element = artwork_element;
9796
9797           SetPlayerWaiting(&stored_player[i], FALSE);
9798
9799           /* set number of special actions for bored and sleeping animation */
9800           stored_player[i].num_special_action_bored =
9801             get_num_special_action(artwork_element,
9802                                    ACTION_BORING_1, ACTION_BORING_LAST);
9803           stored_player[i].num_special_action_sleeping =
9804             get_num_special_action(artwork_element,
9805                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9806         }
9807       }
9808
9809       break;
9810     }
9811
9812     /* ---------- CE actions  ---------------------------------------------- */
9813
9814     case CA_SET_CE_VALUE:
9815     {
9816 #if USE_NEW_CUSTOM_VALUE
9817       int last_ce_value = CustomValue[x][y];
9818
9819       CustomValue[x][y] = action_arg_number_new;
9820
9821       if (CustomValue[x][y] != last_ce_value)
9822       {
9823         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9824         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9825
9826         if (CustomValue[x][y] == 0)
9827         {
9828           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9829           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9830         }
9831       }
9832 #endif
9833
9834       break;
9835     }
9836
9837     case CA_SET_CE_SCORE:
9838     {
9839 #if USE_NEW_CUSTOM_VALUE
9840       int last_ce_score = ei->collect_score;
9841
9842       ei->collect_score = action_arg_number_new;
9843
9844       if (ei->collect_score != last_ce_score)
9845       {
9846         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9847         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9848
9849         if (ei->collect_score == 0)
9850         {
9851           int xx, yy;
9852
9853           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9854           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9855
9856           /*
9857             This is a very special case that seems to be a mixture between
9858             CheckElementChange() and CheckTriggeredElementChange(): while
9859             the first one only affects single elements that are triggered
9860             directly, the second one affects multiple elements in the playfield
9861             that are triggered indirectly by another element. This is a third
9862             case: Changing the CE score always affects multiple identical CEs,
9863             so every affected CE must be checked, not only the single CE for
9864             which the CE score was changed in the first place (as every instance
9865             of that CE shares the same CE score, and therefore also can change)!
9866           */
9867           SCAN_PLAYFIELD(xx, yy)
9868           {
9869             if (Feld[xx][yy] == element)
9870               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9871                                  CE_SCORE_GETS_ZERO);
9872           }
9873         }
9874       }
9875 #endif
9876
9877       break;
9878     }
9879
9880     /* ---------- engine actions  ------------------------------------------ */
9881
9882     case CA_SET_ENGINE_SCAN_MODE:
9883     {
9884       InitPlayfieldScanMode(action_arg);
9885
9886       break;
9887     }
9888
9889     default:
9890       break;
9891   }
9892 }
9893
9894 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9895 {
9896   int old_element = Feld[x][y];
9897   int new_element = GetElementFromGroupElement(element);
9898   int previous_move_direction = MovDir[x][y];
9899 #if USE_NEW_CUSTOM_VALUE
9900   int last_ce_value = CustomValue[x][y];
9901 #endif
9902   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9903   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9904   boolean add_player_onto_element = (new_element_is_player &&
9905 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9906                                      /* this breaks SnakeBite when a snake is
9907                                         halfway through a door that closes */
9908                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9909                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9910 #endif
9911                                      IS_WALKABLE(old_element));
9912
9913 #if 0
9914   /* check if element under the player changes from accessible to unaccessible
9915      (needed for special case of dropping element which then changes) */
9916   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9917       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9918   {
9919     Bang(x, y);
9920
9921     return;
9922   }
9923 #endif
9924
9925   if (!add_player_onto_element)
9926   {
9927     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9928       RemoveMovingField(x, y);
9929     else
9930       RemoveField(x, y);
9931
9932     Feld[x][y] = new_element;
9933
9934 #if !USE_GFX_RESET_GFX_ANIMATION
9935     ResetGfxAnimation(x, y);
9936     ResetRandomAnimationValue(x, y);
9937 #endif
9938
9939     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9940       MovDir[x][y] = previous_move_direction;
9941
9942 #if USE_NEW_CUSTOM_VALUE
9943     if (element_info[new_element].use_last_ce_value)
9944       CustomValue[x][y] = last_ce_value;
9945 #endif
9946
9947     InitField_WithBug1(x, y, FALSE);
9948
9949     new_element = Feld[x][y];   /* element may have changed */
9950
9951 #if USE_GFX_RESET_GFX_ANIMATION
9952     ResetGfxAnimation(x, y);
9953     ResetRandomAnimationValue(x, y);
9954 #endif
9955
9956     DrawLevelField(x, y);
9957
9958     if (GFX_CRUMBLED(new_element))
9959       DrawLevelFieldCrumbledSandNeighbours(x, y);
9960   }
9961
9962 #if 1
9963   /* check if element under the player changes from accessible to unaccessible
9964      (needed for special case of dropping element which then changes) */
9965   /* (must be checked after creating new element for walkable group elements) */
9966 #if USE_FIX_KILLED_BY_NON_WALKABLE
9967   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9968       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9969   {
9970     Bang(x, y);
9971
9972     return;
9973   }
9974 #else
9975   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9976       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9977   {
9978     Bang(x, y);
9979
9980     return;
9981   }
9982 #endif
9983 #endif
9984
9985   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9986   if (new_element_is_player)
9987     RelocatePlayer(x, y, new_element);
9988
9989   if (is_change)
9990     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9991
9992   TestIfBadThingTouchesPlayer(x, y);
9993   TestIfPlayerTouchesCustomElement(x, y);
9994   TestIfElementTouchesCustomElement(x, y);
9995 }
9996
9997 static void CreateField(int x, int y, int element)
9998 {
9999   CreateFieldExt(x, y, element, FALSE);
10000 }
10001
10002 static void CreateElementFromChange(int x, int y, int element)
10003 {
10004   element = GET_VALID_RUNTIME_ELEMENT(element);
10005
10006 #if USE_STOP_CHANGED_ELEMENTS
10007   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10008   {
10009     int old_element = Feld[x][y];
10010
10011     /* prevent changed element from moving in same engine frame
10012        unless both old and new element can either fall or move */
10013     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10014         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10015       Stop[x][y] = TRUE;
10016   }
10017 #endif
10018
10019   CreateFieldExt(x, y, element, TRUE);
10020 }
10021
10022 static boolean ChangeElement(int x, int y, int element, int page)
10023 {
10024   struct ElementInfo *ei = &element_info[element];
10025   struct ElementChangeInfo *change = &ei->change_page[page];
10026   int ce_value = CustomValue[x][y];
10027   int ce_score = ei->collect_score;
10028   int target_element;
10029   int old_element = Feld[x][y];
10030
10031   /* always use default change event to prevent running into a loop */
10032   if (ChangeEvent[x][y] == -1)
10033     ChangeEvent[x][y] = CE_DELAY;
10034
10035   if (ChangeEvent[x][y] == CE_DELAY)
10036   {
10037     /* reset actual trigger element, trigger player and action element */
10038     change->actual_trigger_element = EL_EMPTY;
10039     change->actual_trigger_player = EL_PLAYER_1;
10040     change->actual_trigger_side = CH_SIDE_NONE;
10041     change->actual_trigger_ce_value = 0;
10042     change->actual_trigger_ce_score = 0;
10043   }
10044
10045   /* do not change elements more than a specified maximum number of changes */
10046   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10047     return FALSE;
10048
10049   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10050
10051   if (change->explode)
10052   {
10053     Bang(x, y);
10054
10055     return TRUE;
10056   }
10057
10058   if (change->use_target_content)
10059   {
10060     boolean complete_replace = TRUE;
10061     boolean can_replace[3][3];
10062     int xx, yy;
10063
10064     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10065     {
10066       boolean is_empty;
10067       boolean is_walkable;
10068       boolean is_diggable;
10069       boolean is_collectible;
10070       boolean is_removable;
10071       boolean is_destructible;
10072       int ex = x + xx - 1;
10073       int ey = y + yy - 1;
10074       int content_element = change->target_content.e[xx][yy];
10075       int e;
10076
10077       can_replace[xx][yy] = TRUE;
10078
10079       if (ex == x && ey == y)   /* do not check changing element itself */
10080         continue;
10081
10082       if (content_element == EL_EMPTY_SPACE)
10083       {
10084         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10085
10086         continue;
10087       }
10088
10089       if (!IN_LEV_FIELD(ex, ey))
10090       {
10091         can_replace[xx][yy] = FALSE;
10092         complete_replace = FALSE;
10093
10094         continue;
10095       }
10096
10097       e = Feld[ex][ey];
10098
10099       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10100         e = MovingOrBlocked2Element(ex, ey);
10101
10102       is_empty = (IS_FREE(ex, ey) ||
10103                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10104
10105       is_walkable     = (is_empty || IS_WALKABLE(e));
10106       is_diggable     = (is_empty || IS_DIGGABLE(e));
10107       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10108       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10109       is_removable    = (is_diggable || is_collectible);
10110
10111       can_replace[xx][yy] =
10112         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10113           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10114           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10115           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10116           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10117           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10118          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10119
10120       if (!can_replace[xx][yy])
10121         complete_replace = FALSE;
10122     }
10123
10124     if (!change->only_if_complete || complete_replace)
10125     {
10126       boolean something_has_changed = FALSE;
10127
10128       if (change->only_if_complete && change->use_random_replace &&
10129           RND(100) < change->random_percentage)
10130         return FALSE;
10131
10132       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10133       {
10134         int ex = x + xx - 1;
10135         int ey = y + yy - 1;
10136         int content_element;
10137
10138         if (can_replace[xx][yy] && (!change->use_random_replace ||
10139                                     RND(100) < change->random_percentage))
10140         {
10141           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10142             RemoveMovingField(ex, ey);
10143
10144           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10145
10146           content_element = change->target_content.e[xx][yy];
10147           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10148                                               ce_value, ce_score);
10149
10150           CreateElementFromChange(ex, ey, target_element);
10151
10152           something_has_changed = TRUE;
10153
10154           /* for symmetry reasons, freeze newly created border elements */
10155           if (ex != x || ey != y)
10156             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10157         }
10158       }
10159
10160       if (something_has_changed)
10161       {
10162         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10163         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10164       }
10165     }
10166   }
10167   else
10168   {
10169     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10170                                         ce_value, ce_score);
10171
10172     if (element == EL_DIAGONAL_GROWING ||
10173         element == EL_DIAGONAL_SHRINKING)
10174     {
10175       target_element = Store[x][y];
10176
10177       Store[x][y] = EL_EMPTY;
10178     }
10179
10180     CreateElementFromChange(x, y, target_element);
10181
10182     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10183     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10184   }
10185
10186   /* this uses direct change before indirect change */
10187   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10188
10189   return TRUE;
10190 }
10191
10192 #if USE_NEW_DELAYED_ACTION
10193
10194 static void HandleElementChange(int x, int y, int page)
10195 {
10196   int element = MovingOrBlocked2Element(x, y);
10197   struct ElementInfo *ei = &element_info[element];
10198   struct ElementChangeInfo *change = &ei->change_page[page];
10199
10200 #ifdef DEBUG
10201   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10202       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10203   {
10204     printf("\n\n");
10205     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10206            x, y, element, element_info[element].token_name);
10207     printf("HandleElementChange(): This should never happen!\n");
10208     printf("\n\n");
10209   }
10210 #endif
10211
10212   /* this can happen with classic bombs on walkable, changing elements */
10213   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10214   {
10215 #if 0
10216     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10217       ChangeDelay[x][y] = 0;
10218 #endif
10219
10220     return;
10221   }
10222
10223   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10224   {
10225     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10226
10227     if (change->can_change)
10228     {
10229 #if 1
10230       /* !!! not clear why graphic animation should be reset at all here !!! */
10231       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10232 #if USE_GFX_RESET_WHEN_NOT_MOVING
10233       /* when a custom element is about to change (for example by change delay),
10234          do not reset graphic animation when the custom element is moving */
10235       if (!IS_MOVING(x, y))
10236 #endif
10237       {
10238         ResetGfxAnimation(x, y);
10239         ResetRandomAnimationValue(x, y);
10240       }
10241 #endif
10242
10243       if (change->pre_change_function)
10244         change->pre_change_function(x, y);
10245     }
10246   }
10247
10248   ChangeDelay[x][y]--;
10249
10250   if (ChangeDelay[x][y] != 0)           /* continue element change */
10251   {
10252     if (change->can_change)
10253     {
10254       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255
10256       if (IS_ANIMATED(graphic))
10257         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10258
10259       if (change->change_function)
10260         change->change_function(x, y);
10261     }
10262   }
10263   else                                  /* finish element change */
10264   {
10265     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10266     {
10267       page = ChangePage[x][y];
10268       ChangePage[x][y] = -1;
10269
10270       change = &ei->change_page[page];
10271     }
10272
10273     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10274     {
10275       ChangeDelay[x][y] = 1;            /* try change after next move step */
10276       ChangePage[x][y] = page;          /* remember page to use for change */
10277
10278       return;
10279     }
10280
10281     if (change->can_change)
10282     {
10283       if (ChangeElement(x, y, element, page))
10284       {
10285         if (change->post_change_function)
10286           change->post_change_function(x, y);
10287       }
10288     }
10289
10290     if (change->has_action)
10291       ExecuteCustomElementAction(x, y, element, page);
10292   }
10293 }
10294
10295 #else
10296
10297 static void HandleElementChange(int x, int y, int page)
10298 {
10299   int element = MovingOrBlocked2Element(x, y);
10300   struct ElementInfo *ei = &element_info[element];
10301   struct ElementChangeInfo *change = &ei->change_page[page];
10302
10303 #ifdef DEBUG
10304   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10305   {
10306     printf("\n\n");
10307     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10308            x, y, element, element_info[element].token_name);
10309     printf("HandleElementChange(): This should never happen!\n");
10310     printf("\n\n");
10311   }
10312 #endif
10313
10314   /* this can happen with classic bombs on walkable, changing elements */
10315   if (!CAN_CHANGE(element))
10316   {
10317 #if 0
10318     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10319       ChangeDelay[x][y] = 0;
10320 #endif
10321
10322     return;
10323   }
10324
10325   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10326   {
10327     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10328
10329     ResetGfxAnimation(x, y);
10330     ResetRandomAnimationValue(x, y);
10331
10332     if (change->pre_change_function)
10333       change->pre_change_function(x, y);
10334   }
10335
10336   ChangeDelay[x][y]--;
10337
10338   if (ChangeDelay[x][y] != 0)           /* continue element change */
10339   {
10340     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10341
10342     if (IS_ANIMATED(graphic))
10343       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10344
10345     if (change->change_function)
10346       change->change_function(x, y);
10347   }
10348   else                                  /* finish element change */
10349   {
10350     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10351     {
10352       page = ChangePage[x][y];
10353       ChangePage[x][y] = -1;
10354
10355       change = &ei->change_page[page];
10356     }
10357
10358     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10359     {
10360       ChangeDelay[x][y] = 1;            /* try change after next move step */
10361       ChangePage[x][y] = page;          /* remember page to use for change */
10362
10363       return;
10364     }
10365
10366     if (ChangeElement(x, y, element, page))
10367     {
10368       if (change->post_change_function)
10369         change->post_change_function(x, y);
10370     }
10371   }
10372 }
10373
10374 #endif
10375
10376 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10377                                               int trigger_element,
10378                                               int trigger_event,
10379                                               int trigger_player,
10380                                               int trigger_side,
10381                                               int trigger_page)
10382 {
10383   boolean change_done_any = FALSE;
10384   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10385   int i;
10386
10387   if (!(trigger_events[trigger_element][trigger_event]))
10388     return FALSE;
10389
10390 #if 0
10391   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10392          trigger_event, recursion_loop_depth, recursion_loop_detected,
10393          recursion_loop_element, EL_NAME(recursion_loop_element));
10394 #endif
10395
10396   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10397
10398   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10399   {
10400     int element = EL_CUSTOM_START + i;
10401     boolean change_done = FALSE;
10402     int p;
10403
10404     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10405         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10406       continue;
10407
10408     for (p = 0; p < element_info[element].num_change_pages; p++)
10409     {
10410       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10411
10412       if (change->can_change_or_has_action &&
10413           change->has_event[trigger_event] &&
10414           change->trigger_side & trigger_side &&
10415           change->trigger_player & trigger_player &&
10416           change->trigger_page & trigger_page_bits &&
10417           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10418       {
10419         change->actual_trigger_element = trigger_element;
10420         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10421         change->actual_trigger_side = trigger_side;
10422         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10423         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10424
10425         if ((change->can_change && !change_done) || change->has_action)
10426         {
10427           int x, y;
10428
10429           SCAN_PLAYFIELD(x, y)
10430           {
10431             if (Feld[x][y] == element)
10432             {
10433               if (change->can_change && !change_done)
10434               {
10435                 ChangeDelay[x][y] = 1;
10436                 ChangeEvent[x][y] = trigger_event;
10437
10438                 HandleElementChange(x, y, p);
10439               }
10440 #if USE_NEW_DELAYED_ACTION
10441               else if (change->has_action)
10442               {
10443                 ExecuteCustomElementAction(x, y, element, p);
10444                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10445               }
10446 #else
10447               if (change->has_action)
10448               {
10449                 ExecuteCustomElementAction(x, y, element, p);
10450                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10451               }
10452 #endif
10453             }
10454           }
10455
10456           if (change->can_change)
10457           {
10458             change_done = TRUE;
10459             change_done_any = TRUE;
10460           }
10461         }
10462       }
10463     }
10464   }
10465
10466   RECURSION_LOOP_DETECTION_END();
10467
10468   return change_done_any;
10469 }
10470
10471 static boolean CheckElementChangeExt(int x, int y,
10472                                      int element,
10473                                      int trigger_element,
10474                                      int trigger_event,
10475                                      int trigger_player,
10476                                      int trigger_side)
10477 {
10478   boolean change_done = FALSE;
10479   int p;
10480
10481   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10482       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10483     return FALSE;
10484
10485   if (Feld[x][y] == EL_BLOCKED)
10486   {
10487     Blocked2Moving(x, y, &x, &y);
10488     element = Feld[x][y];
10489   }
10490
10491 #if 0
10492   /* check if element has already changed */
10493   if (Feld[x][y] != element)
10494     return FALSE;
10495 #else
10496   /* check if element has already changed or is about to change after moving */
10497   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10498        Feld[x][y] != element) ||
10499
10500       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10501        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10502         ChangePage[x][y] != -1)))
10503     return FALSE;
10504 #endif
10505
10506 #if 0
10507   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10508          trigger_event, recursion_loop_depth, recursion_loop_detected,
10509          recursion_loop_element, EL_NAME(recursion_loop_element));
10510 #endif
10511
10512   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10513
10514   for (p = 0; p < element_info[element].num_change_pages; p++)
10515   {
10516     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10517
10518     /* check trigger element for all events where the element that is checked
10519        for changing interacts with a directly adjacent element -- this is
10520        different to element changes that affect other elements to change on the
10521        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10522     boolean check_trigger_element =
10523       (trigger_event == CE_TOUCHING_X ||
10524        trigger_event == CE_HITTING_X ||
10525        trigger_event == CE_HIT_BY_X ||
10526 #if 1
10527        /* this one was forgotten until 3.2.3 */
10528        trigger_event == CE_DIGGING_X);
10529 #endif
10530
10531     if (change->can_change_or_has_action &&
10532         change->has_event[trigger_event] &&
10533         change->trigger_side & trigger_side &&
10534         change->trigger_player & trigger_player &&
10535         (!check_trigger_element ||
10536          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10537     {
10538       change->actual_trigger_element = trigger_element;
10539       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10540       change->actual_trigger_side = trigger_side;
10541       change->actual_trigger_ce_value = CustomValue[x][y];
10542       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10543
10544       /* special case: trigger element not at (x,y) position for some events */
10545       if (check_trigger_element)
10546       {
10547         static struct
10548         {
10549           int dx, dy;
10550         } move_xy[] =
10551           {
10552             {  0,  0 },
10553             { -1,  0 },
10554             { +1,  0 },
10555             {  0,  0 },
10556             {  0, -1 },
10557             {  0,  0 }, { 0, 0 }, { 0, 0 },
10558             {  0, +1 }
10559           };
10560
10561         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10562         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10563
10564         change->actual_trigger_ce_value = CustomValue[xx][yy];
10565         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10566       }
10567
10568       if (change->can_change && !change_done)
10569       {
10570         ChangeDelay[x][y] = 1;
10571         ChangeEvent[x][y] = trigger_event;
10572
10573         HandleElementChange(x, y, p);
10574
10575         change_done = TRUE;
10576       }
10577 #if USE_NEW_DELAYED_ACTION
10578       else if (change->has_action)
10579       {
10580         ExecuteCustomElementAction(x, y, element, p);
10581         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10582       }
10583 #else
10584       if (change->has_action)
10585       {
10586         ExecuteCustomElementAction(x, y, element, p);
10587         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10588       }
10589 #endif
10590     }
10591   }
10592
10593   RECURSION_LOOP_DETECTION_END();
10594
10595   return change_done;
10596 }
10597
10598 static void PlayPlayerSound(struct PlayerInfo *player)
10599 {
10600   int jx = player->jx, jy = player->jy;
10601   int sound_element = player->artwork_element;
10602   int last_action = player->last_action_waiting;
10603   int action = player->action_waiting;
10604
10605   if (player->is_waiting)
10606   {
10607     if (action != last_action)
10608       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10609     else
10610       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10611   }
10612   else
10613   {
10614     if (action != last_action)
10615       StopSound(element_info[sound_element].sound[last_action]);
10616
10617     if (last_action == ACTION_SLEEPING)
10618       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10619   }
10620 }
10621
10622 static void PlayAllPlayersSound()
10623 {
10624   int i;
10625
10626   for (i = 0; i < MAX_PLAYERS; i++)
10627     if (stored_player[i].active)
10628       PlayPlayerSound(&stored_player[i]);
10629 }
10630
10631 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10632 {
10633   boolean last_waiting = player->is_waiting;
10634   int move_dir = player->MovDir;
10635
10636   player->dir_waiting = move_dir;
10637   player->last_action_waiting = player->action_waiting;
10638
10639   if (is_waiting)
10640   {
10641     if (!last_waiting)          /* not waiting -> waiting */
10642     {
10643       player->is_waiting = TRUE;
10644
10645       player->frame_counter_bored =
10646         FrameCounter +
10647         game.player_boring_delay_fixed +
10648         GetSimpleRandom(game.player_boring_delay_random);
10649       player->frame_counter_sleeping =
10650         FrameCounter +
10651         game.player_sleeping_delay_fixed +
10652         GetSimpleRandom(game.player_sleeping_delay_random);
10653
10654       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10655     }
10656
10657     if (game.player_sleeping_delay_fixed +
10658         game.player_sleeping_delay_random > 0 &&
10659         player->anim_delay_counter == 0 &&
10660         player->post_delay_counter == 0 &&
10661         FrameCounter >= player->frame_counter_sleeping)
10662       player->is_sleeping = TRUE;
10663     else if (game.player_boring_delay_fixed +
10664              game.player_boring_delay_random > 0 &&
10665              FrameCounter >= player->frame_counter_bored)
10666       player->is_bored = TRUE;
10667
10668     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10669                               player->is_bored ? ACTION_BORING :
10670                               ACTION_WAITING);
10671
10672     if (player->is_sleeping && player->use_murphy)
10673     {
10674       /* special case for sleeping Murphy when leaning against non-free tile */
10675
10676       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10677           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10678            !IS_MOVING(player->jx - 1, player->jy)))
10679         move_dir = MV_LEFT;
10680       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10681                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10682                 !IS_MOVING(player->jx + 1, player->jy)))
10683         move_dir = MV_RIGHT;
10684       else
10685         player->is_sleeping = FALSE;
10686
10687       player->dir_waiting = move_dir;
10688     }
10689
10690     if (player->is_sleeping)
10691     {
10692       if (player->num_special_action_sleeping > 0)
10693       {
10694         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10695         {
10696           int last_special_action = player->special_action_sleeping;
10697           int num_special_action = player->num_special_action_sleeping;
10698           int special_action =
10699             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10700              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10701              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10702              last_special_action + 1 : ACTION_SLEEPING);
10703           int special_graphic =
10704             el_act_dir2img(player->artwork_element, special_action, move_dir);
10705
10706           player->anim_delay_counter =
10707             graphic_info[special_graphic].anim_delay_fixed +
10708             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10709           player->post_delay_counter =
10710             graphic_info[special_graphic].post_delay_fixed +
10711             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10712
10713           player->special_action_sleeping = special_action;
10714         }
10715
10716         if (player->anim_delay_counter > 0)
10717         {
10718           player->action_waiting = player->special_action_sleeping;
10719           player->anim_delay_counter--;
10720         }
10721         else if (player->post_delay_counter > 0)
10722         {
10723           player->post_delay_counter--;
10724         }
10725       }
10726     }
10727     else if (player->is_bored)
10728     {
10729       if (player->num_special_action_bored > 0)
10730       {
10731         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10732         {
10733           int special_action =
10734             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10735           int special_graphic =
10736             el_act_dir2img(player->artwork_element, special_action, move_dir);
10737
10738           player->anim_delay_counter =
10739             graphic_info[special_graphic].anim_delay_fixed +
10740             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10741           player->post_delay_counter =
10742             graphic_info[special_graphic].post_delay_fixed +
10743             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10744
10745           player->special_action_bored = special_action;
10746         }
10747
10748         if (player->anim_delay_counter > 0)
10749         {
10750           player->action_waiting = player->special_action_bored;
10751           player->anim_delay_counter--;
10752         }
10753         else if (player->post_delay_counter > 0)
10754         {
10755           player->post_delay_counter--;
10756         }
10757       }
10758     }
10759   }
10760   else if (last_waiting)        /* waiting -> not waiting */
10761   {
10762     player->is_waiting = FALSE;
10763     player->is_bored = FALSE;
10764     player->is_sleeping = FALSE;
10765
10766     player->frame_counter_bored = -1;
10767     player->frame_counter_sleeping = -1;
10768
10769     player->anim_delay_counter = 0;
10770     player->post_delay_counter = 0;
10771
10772     player->dir_waiting = player->MovDir;
10773     player->action_waiting = ACTION_DEFAULT;
10774
10775     player->special_action_bored = ACTION_DEFAULT;
10776     player->special_action_sleeping = ACTION_DEFAULT;
10777   }
10778 }
10779
10780 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10781 {
10782   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10783   int left      = player_action & JOY_LEFT;
10784   int right     = player_action & JOY_RIGHT;
10785   int up        = player_action & JOY_UP;
10786   int down      = player_action & JOY_DOWN;
10787   int button1   = player_action & JOY_BUTTON_1;
10788   int button2   = player_action & JOY_BUTTON_2;
10789   int dx        = (left ? -1 : right ? 1 : 0);
10790   int dy        = (up   ? -1 : down  ? 1 : 0);
10791
10792   if (!player->active || tape.pausing)
10793     return 0;
10794
10795   if (player_action)
10796   {
10797     if (button1)
10798       snapped = SnapField(player, dx, dy);
10799     else
10800     {
10801       if (button2)
10802         dropped = DropElement(player);
10803
10804       moved = MovePlayer(player, dx, dy);
10805     }
10806
10807     if (tape.single_step && tape.recording && !tape.pausing)
10808     {
10809       if (button1 || (dropped && !moved))
10810       {
10811         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10812         SnapField(player, 0, 0);                /* stop snapping */
10813       }
10814     }
10815
10816     SetPlayerWaiting(player, FALSE);
10817
10818     return player_action;
10819   }
10820   else
10821   {
10822     /* no actions for this player (no input at player's configured device) */
10823
10824     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10825     SnapField(player, 0, 0);
10826     CheckGravityMovementWhenNotMoving(player);
10827
10828     if (player->MovPos == 0)
10829       SetPlayerWaiting(player, TRUE);
10830
10831     if (player->MovPos == 0)    /* needed for tape.playing */
10832       player->is_moving = FALSE;
10833
10834     player->is_dropping = FALSE;
10835     player->is_dropping_pressed = FALSE;
10836     player->drop_pressed_delay = 0;
10837
10838     return 0;
10839   }
10840 }
10841
10842 static void CheckLevelTime()
10843 {
10844   int i;
10845
10846   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10847   {
10848     if (level.native_em_level->lev->home == 0)  /* all players at home */
10849     {
10850       PlayerWins(local_player);
10851
10852       AllPlayersGone = TRUE;
10853
10854       level.native_em_level->lev->home = -1;
10855     }
10856
10857     if (level.native_em_level->ply[0]->alive == 0 &&
10858         level.native_em_level->ply[1]->alive == 0 &&
10859         level.native_em_level->ply[2]->alive == 0 &&
10860         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10861       AllPlayersGone = TRUE;
10862   }
10863
10864   if (TimeFrames >= FRAMES_PER_SECOND)
10865   {
10866     TimeFrames = 0;
10867     TapeTime++;
10868
10869     for (i = 0; i < MAX_PLAYERS; i++)
10870     {
10871       struct PlayerInfo *player = &stored_player[i];
10872
10873       if (SHIELD_ON(player))
10874       {
10875         player->shield_normal_time_left--;
10876
10877         if (player->shield_deadly_time_left > 0)
10878           player->shield_deadly_time_left--;
10879       }
10880     }
10881
10882     if (!local_player->LevelSolved && !level.use_step_counter)
10883     {
10884       TimePlayed++;
10885
10886       if (TimeLeft > 0)
10887       {
10888         TimeLeft--;
10889
10890         if (TimeLeft <= 10 && setup.time_limit)
10891           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10892
10893 #if 1
10894         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10895
10896         DisplayGameControlValues();
10897 #else
10898         DrawGameValue_Time(TimeLeft);
10899 #endif
10900
10901         if (!TimeLeft && setup.time_limit)
10902         {
10903           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10904             level.native_em_level->lev->killed_out_of_time = TRUE;
10905           else
10906             for (i = 0; i < MAX_PLAYERS; i++)
10907               KillPlayer(&stored_player[i]);
10908         }
10909       }
10910 #if 1
10911       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10912       {
10913         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10914
10915         DisplayGameControlValues();
10916       }
10917 #else
10918       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10919         DrawGameValue_Time(TimePlayed);
10920 #endif
10921
10922       level.native_em_level->lev->time =
10923         (level.time == 0 ? TimePlayed : TimeLeft);
10924     }
10925
10926     if (tape.recording || tape.playing)
10927       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10928   }
10929
10930   UpdateGameDoorValues();
10931   DrawGameDoorValues();
10932 }
10933
10934 void AdvanceFrameAndPlayerCounters(int player_nr)
10935 {
10936   int i;
10937
10938   /* advance frame counters (global frame counter and time frame counter) */
10939   FrameCounter++;
10940   TimeFrames++;
10941
10942   /* advance player counters (counters for move delay, move animation etc.) */
10943   for (i = 0; i < MAX_PLAYERS; i++)
10944   {
10945     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10946     int move_delay_value = stored_player[i].move_delay_value;
10947     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10948
10949     if (!advance_player_counters)       /* not all players may be affected */
10950       continue;
10951
10952 #if USE_NEW_PLAYER_ANIM
10953     if (move_frames == 0)       /* less than one move per game frame */
10954     {
10955       int stepsize = TILEX / move_delay_value;
10956       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10957       int count = (stored_player[i].is_moving ?
10958                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10959
10960       if (count % delay == 0)
10961         move_frames = 1;
10962     }
10963 #endif
10964
10965     stored_player[i].Frame += move_frames;
10966
10967     if (stored_player[i].MovPos != 0)
10968       stored_player[i].StepFrame += move_frames;
10969
10970     if (stored_player[i].move_delay > 0)
10971       stored_player[i].move_delay--;
10972
10973     /* due to bugs in previous versions, counter must count up, not down */
10974     if (stored_player[i].push_delay != -1)
10975       stored_player[i].push_delay++;
10976
10977     if (stored_player[i].drop_delay > 0)
10978       stored_player[i].drop_delay--;
10979
10980     if (stored_player[i].is_dropping_pressed)
10981       stored_player[i].drop_pressed_delay++;
10982   }
10983 }
10984
10985 void StartGameActions(boolean init_network_game, boolean record_tape,
10986                       long random_seed)
10987 {
10988   unsigned long new_random_seed = InitRND(random_seed);
10989
10990   if (record_tape)
10991     TapeStartRecording(new_random_seed);
10992
10993 #if defined(NETWORK_AVALIABLE)
10994   if (init_network_game)
10995   {
10996     SendToServer_StartPlaying();
10997
10998     return;
10999   }
11000 #endif
11001
11002   InitGame();
11003 }
11004
11005 void GameActions()
11006 {
11007   static unsigned long game_frame_delay = 0;
11008   unsigned long game_frame_delay_value;
11009   byte *recorded_player_action;
11010   byte summarized_player_action = 0;
11011   byte tape_action[MAX_PLAYERS];
11012   int i;
11013
11014   /* detect endless loops, caused by custom element programming */
11015   if (recursion_loop_detected && recursion_loop_depth == 0)
11016   {
11017     char *message = getStringCat3("Internal Error ! Element ",
11018                                   EL_NAME(recursion_loop_element),
11019                                   " caused endless loop ! Quit the game ?");
11020
11021     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11022           EL_NAME(recursion_loop_element));
11023
11024     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11025
11026     recursion_loop_detected = FALSE;    /* if game should be continued */
11027
11028     free(message);
11029
11030     return;
11031   }
11032
11033   if (game.restart_level)
11034     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11035
11036   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11037   {
11038     if (level.native_em_level->lev->home == 0)  /* all players at home */
11039     {
11040       PlayerWins(local_player);
11041
11042       AllPlayersGone = TRUE;
11043
11044       level.native_em_level->lev->home = -1;
11045     }
11046
11047     if (level.native_em_level->ply[0]->alive == 0 &&
11048         level.native_em_level->ply[1]->alive == 0 &&
11049         level.native_em_level->ply[2]->alive == 0 &&
11050         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11051       AllPlayersGone = TRUE;
11052   }
11053
11054   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11055     GameWon();
11056
11057   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11058     TapeStop();
11059
11060   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11061     return;
11062
11063   game_frame_delay_value =
11064     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11065
11066   if (tape.playing && tape.warp_forward && !tape.pausing)
11067     game_frame_delay_value = 0;
11068
11069   /* ---------- main game synchronization point ---------- */
11070
11071   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11072
11073   if (network_playing && !network_player_action_received)
11074   {
11075     /* try to get network player actions in time */
11076
11077 #if defined(NETWORK_AVALIABLE)
11078     /* last chance to get network player actions without main loop delay */
11079     HandleNetworking();
11080 #endif
11081
11082     /* game was quit by network peer */
11083     if (game_status != GAME_MODE_PLAYING)
11084       return;
11085
11086     if (!network_player_action_received)
11087       return;           /* failed to get network player actions in time */
11088
11089     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11090   }
11091
11092   if (tape.pausing)
11093     return;
11094
11095   /* at this point we know that we really continue executing the game */
11096
11097   network_player_action_received = FALSE;
11098
11099   /* when playing tape, read previously recorded player input from tape data */
11100   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11101
11102 #if 1
11103   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11104   if (tape.pausing)
11105     return;
11106 #endif
11107
11108   if (tape.set_centered_player)
11109   {
11110     game.centered_player_nr_next = tape.centered_player_nr_next;
11111     game.set_centered_player = TRUE;
11112   }
11113
11114   for (i = 0; i < MAX_PLAYERS; i++)
11115   {
11116     summarized_player_action |= stored_player[i].action;
11117
11118     if (!network_playing)
11119       stored_player[i].effective_action = stored_player[i].action;
11120   }
11121
11122 #if defined(NETWORK_AVALIABLE)
11123   if (network_playing)
11124     SendToServer_MovePlayer(summarized_player_action);
11125 #endif
11126
11127   if (!options.network && !setup.team_mode)
11128     local_player->effective_action = summarized_player_action;
11129
11130   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11131   {
11132     for (i = 0; i < MAX_PLAYERS; i++)
11133       stored_player[i].effective_action =
11134         (i == game.centered_player_nr ? summarized_player_action : 0);
11135   }
11136
11137   if (recorded_player_action != NULL)
11138     for (i = 0; i < MAX_PLAYERS; i++)
11139       stored_player[i].effective_action = recorded_player_action[i];
11140
11141   for (i = 0; i < MAX_PLAYERS; i++)
11142   {
11143     tape_action[i] = stored_player[i].effective_action;
11144
11145     /* (this can only happen in the R'n'D game engine) */
11146     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11147       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11148   }
11149
11150   /* only record actions from input devices, but not programmed actions */
11151   if (tape.recording)
11152     TapeRecordAction(tape_action);
11153
11154   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11155   {
11156     GameActions_EM_Main();
11157   }
11158   else
11159   {
11160     GameActions_RND();
11161   }
11162 }
11163
11164 void GameActions_EM_Main()
11165 {
11166   byte effective_action[MAX_PLAYERS];
11167   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11168   int i;
11169
11170   for (i = 0; i < MAX_PLAYERS; i++)
11171     effective_action[i] = stored_player[i].effective_action;
11172
11173   GameActions_EM(effective_action, warp_mode);
11174
11175   CheckLevelTime();
11176
11177   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11178 }
11179
11180 void GameActions_RND()
11181 {
11182   int magic_wall_x = 0, magic_wall_y = 0;
11183   int i, x, y, element, graphic;
11184
11185   InitPlayfieldScanModeVars();
11186
11187 #if USE_ONE_MORE_CHANGE_PER_FRAME
11188   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11189   {
11190     SCAN_PLAYFIELD(x, y)
11191     {
11192       ChangeCount[x][y] = 0;
11193       ChangeEvent[x][y] = -1;
11194     }
11195   }
11196 #endif
11197
11198   if (game.set_centered_player)
11199   {
11200     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11201
11202     /* switching to "all players" only possible if all players fit to screen */
11203     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11204     {
11205       game.centered_player_nr_next = game.centered_player_nr;
11206       game.set_centered_player = FALSE;
11207     }
11208
11209     /* do not switch focus to non-existing (or non-active) player */
11210     if (game.centered_player_nr_next >= 0 &&
11211         !stored_player[game.centered_player_nr_next].active)
11212     {
11213       game.centered_player_nr_next = game.centered_player_nr;
11214       game.set_centered_player = FALSE;
11215     }
11216   }
11217
11218   if (game.set_centered_player &&
11219       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11220   {
11221     int sx, sy;
11222
11223     if (game.centered_player_nr_next == -1)
11224     {
11225       setScreenCenteredToAllPlayers(&sx, &sy);
11226     }
11227     else
11228     {
11229       sx = stored_player[game.centered_player_nr_next].jx;
11230       sy = stored_player[game.centered_player_nr_next].jy;
11231     }
11232
11233     game.centered_player_nr = game.centered_player_nr_next;
11234     game.set_centered_player = FALSE;
11235
11236     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11237     DrawGameDoorValues();
11238   }
11239
11240   for (i = 0; i < MAX_PLAYERS; i++)
11241   {
11242     int actual_player_action = stored_player[i].effective_action;
11243
11244 #if 1
11245     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11246        - rnd_equinox_tetrachloride 048
11247        - rnd_equinox_tetrachloride_ii 096
11248        - rnd_emanuel_schmieg 002
11249        - doctor_sloan_ww 001, 020
11250     */
11251     if (stored_player[i].MovPos == 0)
11252       CheckGravityMovement(&stored_player[i]);
11253 #endif
11254
11255     /* overwrite programmed action with tape action */
11256     if (stored_player[i].programmed_action)
11257       actual_player_action = stored_player[i].programmed_action;
11258
11259     PlayerActions(&stored_player[i], actual_player_action);
11260
11261     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11262   }
11263
11264   ScrollScreen(NULL, SCROLL_GO_ON);
11265
11266   /* for backwards compatibility, the following code emulates a fixed bug that
11267      occured when pushing elements (causing elements that just made their last
11268      pushing step to already (if possible) make their first falling step in the
11269      same game frame, which is bad); this code is also needed to use the famous
11270      "spring push bug" which is used in older levels and might be wanted to be
11271      used also in newer levels, but in this case the buggy pushing code is only
11272      affecting the "spring" element and no other elements */
11273
11274   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11275   {
11276     for (i = 0; i < MAX_PLAYERS; i++)
11277     {
11278       struct PlayerInfo *player = &stored_player[i];
11279       int x = player->jx;
11280       int y = player->jy;
11281
11282       if (player->active && player->is_pushing && player->is_moving &&
11283           IS_MOVING(x, y) &&
11284           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11285            Feld[x][y] == EL_SPRING))
11286       {
11287         ContinueMoving(x, y);
11288
11289         /* continue moving after pushing (this is actually a bug) */
11290         if (!IS_MOVING(x, y))
11291           Stop[x][y] = FALSE;
11292       }
11293     }
11294   }
11295
11296 #if 0
11297   debug_print_timestamp(0, "start main loop profiling");
11298 #endif
11299
11300   SCAN_PLAYFIELD(x, y)
11301   {
11302     ChangeCount[x][y] = 0;
11303     ChangeEvent[x][y] = -1;
11304
11305     /* this must be handled before main playfield loop */
11306     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11307     {
11308       MovDelay[x][y]--;
11309       if (MovDelay[x][y] <= 0)
11310         RemoveField(x, y);
11311     }
11312
11313 #if USE_NEW_SNAP_DELAY
11314     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11315     {
11316       MovDelay[x][y]--;
11317       if (MovDelay[x][y] <= 0)
11318       {
11319         RemoveField(x, y);
11320         DrawLevelField(x, y);
11321
11322         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11323       }
11324     }
11325 #endif
11326
11327 #if DEBUG
11328     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11329     {
11330       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11331       printf("GameActions(): This should never happen!\n");
11332
11333       ChangePage[x][y] = -1;
11334     }
11335 #endif
11336
11337     Stop[x][y] = FALSE;
11338     if (WasJustMoving[x][y] > 0)
11339       WasJustMoving[x][y]--;
11340     if (WasJustFalling[x][y] > 0)
11341       WasJustFalling[x][y]--;
11342     if (CheckCollision[x][y] > 0)
11343       CheckCollision[x][y]--;
11344     if (CheckImpact[x][y] > 0)
11345       CheckImpact[x][y]--;
11346
11347     GfxFrame[x][y]++;
11348
11349     /* reset finished pushing action (not done in ContinueMoving() to allow
11350        continuous pushing animation for elements with zero push delay) */
11351     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11352     {
11353       ResetGfxAnimation(x, y);
11354       DrawLevelField(x, y);
11355     }
11356
11357 #if DEBUG
11358     if (IS_BLOCKED(x, y))
11359     {
11360       int oldx, oldy;
11361
11362       Blocked2Moving(x, y, &oldx, &oldy);
11363       if (!IS_MOVING(oldx, oldy))
11364       {
11365         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11366         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11367         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11368         printf("GameActions(): This should never happen!\n");
11369       }
11370     }
11371 #endif
11372   }
11373
11374 #if 0
11375   debug_print_timestamp(0, "- time for pre-main loop:");
11376 #endif
11377
11378 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11379   SCAN_PLAYFIELD(x, y)
11380   {
11381     element = Feld[x][y];
11382     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11383
11384 #if 1
11385     {
11386 #if 1
11387       int element2 = element;
11388       int graphic2 = graphic;
11389 #else
11390       int element2 = Feld[x][y];
11391       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11392 #endif
11393       int last_gfx_frame = GfxFrame[x][y];
11394
11395       if (graphic_info[graphic2].anim_global_sync)
11396         GfxFrame[x][y] = FrameCounter;
11397       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11398         GfxFrame[x][y] = CustomValue[x][y];
11399       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11400         GfxFrame[x][y] = element_info[element2].collect_score;
11401       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11402         GfxFrame[x][y] = ChangeDelay[x][y];
11403
11404       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11405         DrawLevelGraphicAnimation(x, y, graphic2);
11406     }
11407 #else
11408     ResetGfxFrame(x, y, TRUE);
11409 #endif
11410
11411 #if 1
11412     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11413         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11414       ResetRandomAnimationValue(x, y);
11415 #endif
11416
11417 #if 1
11418     SetRandomAnimationValue(x, y);
11419 #endif
11420
11421 #if 1
11422     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11423 #endif
11424   }
11425 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11426
11427 #if 0
11428   debug_print_timestamp(0, "- time for TEST loop:     -->");
11429 #endif
11430
11431   SCAN_PLAYFIELD(x, y)
11432   {
11433     element = Feld[x][y];
11434     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11435
11436     ResetGfxFrame(x, y, TRUE);
11437
11438     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11439         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11440       ResetRandomAnimationValue(x, y);
11441
11442     SetRandomAnimationValue(x, y);
11443
11444     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11445
11446     if (IS_INACTIVE(element))
11447     {
11448       if (IS_ANIMATED(graphic))
11449         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11450
11451       continue;
11452     }
11453
11454     /* this may take place after moving, so 'element' may have changed */
11455     if (IS_CHANGING(x, y) &&
11456         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11457     {
11458       int page = element_info[element].event_page_nr[CE_DELAY];
11459
11460 #if 1
11461       HandleElementChange(x, y, page);
11462 #else
11463       if (CAN_CHANGE(element))
11464         HandleElementChange(x, y, page);
11465
11466       if (HAS_ACTION(element))
11467         ExecuteCustomElementAction(x, y, element, page);
11468 #endif
11469
11470       element = Feld[x][y];
11471       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11472     }
11473
11474 #if 0   // ---------------------------------------------------------------------
11475
11476     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11477     {
11478       StartMoving(x, y);
11479
11480       element = Feld[x][y];
11481       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11482
11483       if (IS_ANIMATED(graphic) &&
11484           !IS_MOVING(x, y) &&
11485           !Stop[x][y])
11486         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11487
11488       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11489         DrawTwinkleOnField(x, y);
11490     }
11491     else if (IS_MOVING(x, y))
11492       ContinueMoving(x, y);
11493     else
11494     {
11495       switch (element)
11496       {
11497         case EL_ACID:
11498         case EL_EXIT_OPEN:
11499         case EL_EM_EXIT_OPEN:
11500         case EL_SP_EXIT_OPEN:
11501         case EL_STEEL_EXIT_OPEN:
11502         case EL_EM_STEEL_EXIT_OPEN:
11503         case EL_SP_TERMINAL:
11504         case EL_SP_TERMINAL_ACTIVE:
11505         case EL_EXTRA_TIME:
11506         case EL_SHIELD_NORMAL:
11507         case EL_SHIELD_DEADLY:
11508           if (IS_ANIMATED(graphic))
11509             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11510           break;
11511
11512         case EL_DYNAMITE_ACTIVE:
11513         case EL_EM_DYNAMITE_ACTIVE:
11514         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11515         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11516         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11517         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11518         case EL_SP_DISK_RED_ACTIVE:
11519           CheckDynamite(x, y);
11520           break;
11521
11522         case EL_AMOEBA_GROWING:
11523           AmoebeWaechst(x, y);
11524           break;
11525
11526         case EL_AMOEBA_SHRINKING:
11527           AmoebaDisappearing(x, y);
11528           break;
11529
11530 #if !USE_NEW_AMOEBA_CODE
11531         case EL_AMOEBA_WET:
11532         case EL_AMOEBA_DRY:
11533         case EL_AMOEBA_FULL:
11534         case EL_BD_AMOEBA:
11535         case EL_EMC_DRIPPER:
11536           AmoebeAbleger(x, y);
11537           break;
11538 #endif
11539
11540         case EL_GAME_OF_LIFE:
11541         case EL_BIOMAZE:
11542           Life(x, y);
11543           break;
11544
11545         case EL_EXIT_CLOSED:
11546           CheckExit(x, y);
11547           break;
11548
11549         case EL_EM_EXIT_CLOSED:
11550           CheckExitEM(x, y);
11551           break;
11552
11553         case EL_STEEL_EXIT_CLOSED:
11554           CheckExitSteel(x, y);
11555           break;
11556
11557         case EL_EM_STEEL_EXIT_CLOSED:
11558           CheckExitSteelEM(x, y);
11559           break;
11560
11561         case EL_SP_EXIT_CLOSED:
11562           CheckExitSP(x, y);
11563           break;
11564
11565         case EL_EXPANDABLE_WALL_GROWING:
11566         case EL_EXPANDABLE_STEELWALL_GROWING:
11567           MauerWaechst(x, y);
11568           break;
11569
11570         case EL_EXPANDABLE_WALL:
11571         case EL_EXPANDABLE_WALL_HORIZONTAL:
11572         case EL_EXPANDABLE_WALL_VERTICAL:
11573         case EL_EXPANDABLE_WALL_ANY:
11574         case EL_BD_EXPANDABLE_WALL:
11575           MauerAbleger(x, y);
11576           break;
11577
11578         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11579         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11580         case EL_EXPANDABLE_STEELWALL_ANY:
11581           MauerAblegerStahl(x, y);
11582           break;
11583
11584         case EL_FLAMES:
11585           CheckForDragon(x, y);
11586           break;
11587
11588         case EL_EXPLOSION:
11589           break;
11590
11591         case EL_ELEMENT_SNAPPING:
11592         case EL_DIAGONAL_SHRINKING:
11593         case EL_DIAGONAL_GROWING:
11594         {
11595           graphic =
11596             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11597
11598           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11599           break;
11600         }
11601
11602         default:
11603           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11604             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11605           break;
11606       }
11607     }
11608
11609 #else   // ---------------------------------------------------------------------
11610
11611     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11612     {
11613       StartMoving(x, y);
11614
11615       element = Feld[x][y];
11616       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11617
11618       if (IS_ANIMATED(graphic) &&
11619           !IS_MOVING(x, y) &&
11620           !Stop[x][y])
11621         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11622
11623       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11624         DrawTwinkleOnField(x, y);
11625     }
11626     else if ((element == EL_ACID ||
11627               element == EL_EXIT_OPEN ||
11628               element == EL_EM_EXIT_OPEN ||
11629               element == EL_SP_EXIT_OPEN ||
11630               element == EL_STEEL_EXIT_OPEN ||
11631               element == EL_EM_STEEL_EXIT_OPEN ||
11632               element == EL_SP_TERMINAL ||
11633               element == EL_SP_TERMINAL_ACTIVE ||
11634               element == EL_EXTRA_TIME ||
11635               element == EL_SHIELD_NORMAL ||
11636               element == EL_SHIELD_DEADLY) &&
11637              IS_ANIMATED(graphic))
11638       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11639     else if (IS_MOVING(x, y))
11640       ContinueMoving(x, y);
11641     else if (IS_ACTIVE_BOMB(element))
11642       CheckDynamite(x, y);
11643     else if (element == EL_AMOEBA_GROWING)
11644       AmoebeWaechst(x, y);
11645     else if (element == EL_AMOEBA_SHRINKING)
11646       AmoebaDisappearing(x, y);
11647
11648 #if !USE_NEW_AMOEBA_CODE
11649     else if (IS_AMOEBALIVE(element))
11650       AmoebeAbleger(x, y);
11651 #endif
11652
11653     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11654       Life(x, y);
11655     else if (element == EL_EXIT_CLOSED)
11656       CheckExit(x, y);
11657     else if (element == EL_EM_EXIT_CLOSED)
11658       CheckExitEM(x, y);
11659     else if (element == EL_STEEL_EXIT_CLOSED)
11660       CheckExitSteel(x, y);
11661     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11662       CheckExitSteelEM(x, y);
11663     else if (element == EL_SP_EXIT_CLOSED)
11664       CheckExitSP(x, y);
11665     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11666              element == EL_EXPANDABLE_STEELWALL_GROWING)
11667       MauerWaechst(x, y);
11668     else if (element == EL_EXPANDABLE_WALL ||
11669              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11670              element == EL_EXPANDABLE_WALL_VERTICAL ||
11671              element == EL_EXPANDABLE_WALL_ANY ||
11672              element == EL_BD_EXPANDABLE_WALL)
11673       MauerAbleger(x, y);
11674     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11675              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11676              element == EL_EXPANDABLE_STEELWALL_ANY)
11677       MauerAblegerStahl(x, y);
11678     else if (element == EL_FLAMES)
11679       CheckForDragon(x, y);
11680     else if (element == EL_EXPLOSION)
11681       ; /* drawing of correct explosion animation is handled separately */
11682     else if (element == EL_ELEMENT_SNAPPING ||
11683              element == EL_DIAGONAL_SHRINKING ||
11684              element == EL_DIAGONAL_GROWING)
11685     {
11686       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11687
11688       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11689     }
11690     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11691       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11692
11693 #endif  // ---------------------------------------------------------------------
11694
11695     if (IS_BELT_ACTIVE(element))
11696       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11697
11698     if (game.magic_wall_active)
11699     {
11700       int jx = local_player->jx, jy = local_player->jy;
11701
11702       /* play the element sound at the position nearest to the player */
11703       if ((element == EL_MAGIC_WALL_FULL ||
11704            element == EL_MAGIC_WALL_ACTIVE ||
11705            element == EL_MAGIC_WALL_EMPTYING ||
11706            element == EL_BD_MAGIC_WALL_FULL ||
11707            element == EL_BD_MAGIC_WALL_ACTIVE ||
11708            element == EL_BD_MAGIC_WALL_EMPTYING ||
11709            element == EL_DC_MAGIC_WALL_FULL ||
11710            element == EL_DC_MAGIC_WALL_ACTIVE ||
11711            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11712           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11713       {
11714         magic_wall_x = x;
11715         magic_wall_y = y;
11716       }
11717     }
11718   }
11719
11720 #if 0
11721   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11722 #endif
11723
11724 #if USE_NEW_AMOEBA_CODE
11725   /* new experimental amoeba growth stuff */
11726   if (!(FrameCounter % 8))
11727   {
11728     static unsigned long random = 1684108901;
11729
11730     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11731     {
11732       x = RND(lev_fieldx);
11733       y = RND(lev_fieldy);
11734       element = Feld[x][y];
11735
11736       if (!IS_PLAYER(x,y) &&
11737           (element == EL_EMPTY ||
11738            CAN_GROW_INTO(element) ||
11739            element == EL_QUICKSAND_EMPTY ||
11740            element == EL_QUICKSAND_FAST_EMPTY ||
11741            element == EL_ACID_SPLASH_LEFT ||
11742            element == EL_ACID_SPLASH_RIGHT))
11743       {
11744         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11745             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11746             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11747             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11748           Feld[x][y] = EL_AMOEBA_DROP;
11749       }
11750
11751       random = random * 129 + 1;
11752     }
11753   }
11754 #endif
11755
11756 #if 0
11757   if (game.explosions_delayed)
11758 #endif
11759   {
11760     game.explosions_delayed = FALSE;
11761
11762     SCAN_PLAYFIELD(x, y)
11763     {
11764       element = Feld[x][y];
11765
11766       if (ExplodeField[x][y])
11767         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11768       else if (element == EL_EXPLOSION)
11769         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11770
11771       ExplodeField[x][y] = EX_TYPE_NONE;
11772     }
11773
11774     game.explosions_delayed = TRUE;
11775   }
11776
11777   if (game.magic_wall_active)
11778   {
11779     if (!(game.magic_wall_time_left % 4))
11780     {
11781       int element = Feld[magic_wall_x][magic_wall_y];
11782
11783       if (element == EL_BD_MAGIC_WALL_FULL ||
11784           element == EL_BD_MAGIC_WALL_ACTIVE ||
11785           element == EL_BD_MAGIC_WALL_EMPTYING)
11786         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11787       else if (element == EL_DC_MAGIC_WALL_FULL ||
11788                element == EL_DC_MAGIC_WALL_ACTIVE ||
11789                element == EL_DC_MAGIC_WALL_EMPTYING)
11790         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11791       else
11792         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11793     }
11794
11795     if (game.magic_wall_time_left > 0)
11796     {
11797       game.magic_wall_time_left--;
11798
11799       if (!game.magic_wall_time_left)
11800       {
11801         SCAN_PLAYFIELD(x, y)
11802         {
11803           element = Feld[x][y];
11804
11805           if (element == EL_MAGIC_WALL_ACTIVE ||
11806               element == EL_MAGIC_WALL_FULL)
11807           {
11808             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11809             DrawLevelField(x, y);
11810           }
11811           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11812                    element == EL_BD_MAGIC_WALL_FULL)
11813           {
11814             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11815             DrawLevelField(x, y);
11816           }
11817           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11818                    element == EL_DC_MAGIC_WALL_FULL)
11819           {
11820             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11821             DrawLevelField(x, y);
11822           }
11823         }
11824
11825         game.magic_wall_active = FALSE;
11826       }
11827     }
11828   }
11829
11830   if (game.light_time_left > 0)
11831   {
11832     game.light_time_left--;
11833
11834     if (game.light_time_left == 0)
11835       RedrawAllLightSwitchesAndInvisibleElements();
11836   }
11837
11838   if (game.timegate_time_left > 0)
11839   {
11840     game.timegate_time_left--;
11841
11842     if (game.timegate_time_left == 0)
11843       CloseAllOpenTimegates();
11844   }
11845
11846   if (game.lenses_time_left > 0)
11847   {
11848     game.lenses_time_left--;
11849
11850     if (game.lenses_time_left == 0)
11851       RedrawAllInvisibleElementsForLenses();
11852   }
11853
11854   if (game.magnify_time_left > 0)
11855   {
11856     game.magnify_time_left--;
11857
11858     if (game.magnify_time_left == 0)
11859       RedrawAllInvisibleElementsForMagnifier();
11860   }
11861
11862   for (i = 0; i < MAX_PLAYERS; i++)
11863   {
11864     struct PlayerInfo *player = &stored_player[i];
11865
11866     if (SHIELD_ON(player))
11867     {
11868       if (player->shield_deadly_time_left)
11869         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11870       else if (player->shield_normal_time_left)
11871         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11872     }
11873   }
11874
11875   CheckLevelTime();
11876
11877   DrawAllPlayers();
11878   PlayAllPlayersSound();
11879
11880   if (options.debug)                    /* calculate frames per second */
11881   {
11882     static unsigned long fps_counter = 0;
11883     static int fps_frames = 0;
11884     unsigned long fps_delay_ms = Counter() - fps_counter;
11885
11886     fps_frames++;
11887
11888     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11889     {
11890       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11891
11892       fps_frames = 0;
11893       fps_counter = Counter();
11894     }
11895
11896     redraw_mask |= REDRAW_FPS;
11897   }
11898
11899   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11900
11901   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11902   {
11903     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11904
11905     local_player->show_envelope = 0;
11906   }
11907
11908 #if 0
11909   debug_print_timestamp(0, "stop main loop profiling ");
11910   printf("----------------------------------------------------------\n");
11911 #endif
11912
11913   /* use random number generator in every frame to make it less predictable */
11914   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11915     RND(1);
11916 }
11917
11918 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11919 {
11920   int min_x = x, min_y = y, max_x = x, max_y = y;
11921   int i;
11922
11923   for (i = 0; i < MAX_PLAYERS; i++)
11924   {
11925     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11926
11927     if (!stored_player[i].active || &stored_player[i] == player)
11928       continue;
11929
11930     min_x = MIN(min_x, jx);
11931     min_y = MIN(min_y, jy);
11932     max_x = MAX(max_x, jx);
11933     max_y = MAX(max_y, jy);
11934   }
11935
11936   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11937 }
11938
11939 static boolean AllPlayersInVisibleScreen()
11940 {
11941   int i;
11942
11943   for (i = 0; i < MAX_PLAYERS; i++)
11944   {
11945     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11946
11947     if (!stored_player[i].active)
11948       continue;
11949
11950     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11951       return FALSE;
11952   }
11953
11954   return TRUE;
11955 }
11956
11957 void ScrollLevel(int dx, int dy)
11958 {
11959 #if 1
11960   static Bitmap *bitmap_db_field2 = NULL;
11961   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11962   int x, y;
11963 #else
11964   int i, x, y;
11965 #endif
11966
11967 #if 0
11968   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11969   /* only horizontal XOR vertical scroll direction allowed */
11970   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11971     return;
11972 #endif
11973
11974 #if 1
11975   if (bitmap_db_field2 == NULL)
11976     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11977
11978   /* needed when blitting directly to same bitmap -- should not be needed with
11979      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11980   BlitBitmap(drawto_field, bitmap_db_field2,
11981              FX + TILEX * (dx == -1) - softscroll_offset,
11982              FY + TILEY * (dy == -1) - softscroll_offset,
11983              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11984              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11985              FX + TILEX * (dx == 1) - softscroll_offset,
11986              FY + TILEY * (dy == 1) - softscroll_offset);
11987   BlitBitmap(bitmap_db_field2, drawto_field,
11988              FX + TILEX * (dx == 1) - softscroll_offset,
11989              FY + TILEY * (dy == 1) - softscroll_offset,
11990              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11991              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11992              FX + TILEX * (dx == 1) - softscroll_offset,
11993              FY + TILEY * (dy == 1) - softscroll_offset);
11994
11995 #else
11996
11997 #if 1
11998   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11999   int xsize = (BX2 - BX1 + 1);
12000   int ysize = (BY2 - BY1 + 1);
12001   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12002   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12003   int step  = (start < end ? +1 : -1);
12004
12005   for (i = start; i != end; i += step)
12006   {
12007     BlitBitmap(drawto_field, drawto_field,
12008                FX + TILEX * (dx != 0 ? i + step : 0),
12009                FY + TILEY * (dy != 0 ? i + step : 0),
12010                TILEX * (dx != 0 ? 1 : xsize),
12011                TILEY * (dy != 0 ? 1 : ysize),
12012                FX + TILEX * (dx != 0 ? i : 0),
12013                FY + TILEY * (dy != 0 ? i : 0));
12014   }
12015
12016 #else
12017
12018   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12019
12020   BlitBitmap(drawto_field, drawto_field,
12021              FX + TILEX * (dx == -1) - softscroll_offset,
12022              FY + TILEY * (dy == -1) - softscroll_offset,
12023              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12024              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12025              FX + TILEX * (dx == 1) - softscroll_offset,
12026              FY + TILEY * (dy == 1) - softscroll_offset);
12027 #endif
12028 #endif
12029
12030   if (dx != 0)
12031   {
12032     x = (dx == 1 ? BX1 : BX2);
12033     for (y = BY1; y <= BY2; y++)
12034       DrawScreenField(x, y);
12035   }
12036
12037   if (dy != 0)
12038   {
12039     y = (dy == 1 ? BY1 : BY2);
12040     for (x = BX1; x <= BX2; x++)
12041       DrawScreenField(x, y);
12042   }
12043
12044   redraw_mask |= REDRAW_FIELD;
12045 }
12046
12047 static boolean canFallDown(struct PlayerInfo *player)
12048 {
12049   int jx = player->jx, jy = player->jy;
12050
12051   return (IN_LEV_FIELD(jx, jy + 1) &&
12052           (IS_FREE(jx, jy + 1) ||
12053            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12054           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12055           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12056 }
12057
12058 static boolean canPassField(int x, int y, int move_dir)
12059 {
12060   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12061   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12062   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12063   int nextx = x + dx;
12064   int nexty = y + dy;
12065   int element = Feld[x][y];
12066
12067   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12068           !CAN_MOVE(element) &&
12069           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12070           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12071           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12072 }
12073
12074 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12075 {
12076   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12077   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12078   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12079   int newx = x + dx;
12080   int newy = y + dy;
12081
12082   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12083           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12084           (IS_DIGGABLE(Feld[newx][newy]) ||
12085            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12086            canPassField(newx, newy, move_dir)));
12087 }
12088
12089 static void CheckGravityMovement(struct PlayerInfo *player)
12090 {
12091 #if USE_PLAYER_GRAVITY
12092   if (player->gravity && !player->programmed_action)
12093 #else
12094   if (game.gravity && !player->programmed_action)
12095 #endif
12096   {
12097     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12098     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12099     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12100     int jx = player->jx, jy = player->jy;
12101     boolean player_is_moving_to_valid_field =
12102       (!player_is_snapping &&
12103        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12104         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12105     boolean player_can_fall_down = canFallDown(player);
12106
12107     if (player_can_fall_down &&
12108         !player_is_moving_to_valid_field)
12109       player->programmed_action = MV_DOWN;
12110   }
12111 }
12112
12113 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12114 {
12115   return CheckGravityMovement(player);
12116
12117 #if USE_PLAYER_GRAVITY
12118   if (player->gravity && !player->programmed_action)
12119 #else
12120   if (game.gravity && !player->programmed_action)
12121 #endif
12122   {
12123     int jx = player->jx, jy = player->jy;
12124     boolean field_under_player_is_free =
12125       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12126     boolean player_is_standing_on_valid_field =
12127       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12128        (IS_WALKABLE(Feld[jx][jy]) &&
12129         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12130
12131     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12132       player->programmed_action = MV_DOWN;
12133   }
12134 }
12135
12136 /*
12137   MovePlayerOneStep()
12138   -----------------------------------------------------------------------------
12139   dx, dy:               direction (non-diagonal) to try to move the player to
12140   real_dx, real_dy:     direction as read from input device (can be diagonal)
12141 */
12142
12143 boolean MovePlayerOneStep(struct PlayerInfo *player,
12144                           int dx, int dy, int real_dx, int real_dy)
12145 {
12146   int jx = player->jx, jy = player->jy;
12147   int new_jx = jx + dx, new_jy = jy + dy;
12148 #if !USE_FIXED_DONT_RUN_INTO
12149   int element;
12150 #endif
12151   int can_move;
12152   boolean player_can_move = !player->cannot_move;
12153
12154   if (!player->active || (!dx && !dy))
12155     return MP_NO_ACTION;
12156
12157   player->MovDir = (dx < 0 ? MV_LEFT :
12158                     dx > 0 ? MV_RIGHT :
12159                     dy < 0 ? MV_UP :
12160                     dy > 0 ? MV_DOWN :  MV_NONE);
12161
12162   if (!IN_LEV_FIELD(new_jx, new_jy))
12163     return MP_NO_ACTION;
12164
12165   if (!player_can_move)
12166   {
12167     if (player->MovPos == 0)
12168     {
12169       player->is_moving = FALSE;
12170       player->is_digging = FALSE;
12171       player->is_collecting = FALSE;
12172       player->is_snapping = FALSE;
12173       player->is_pushing = FALSE;
12174     }
12175   }
12176
12177 #if 1
12178   if (!options.network && game.centered_player_nr == -1 &&
12179       !AllPlayersInSight(player, new_jx, new_jy))
12180     return MP_NO_ACTION;
12181 #else
12182   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12183     return MP_NO_ACTION;
12184 #endif
12185
12186 #if !USE_FIXED_DONT_RUN_INTO
12187   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12188
12189   /* (moved to DigField()) */
12190   if (player_can_move && DONT_RUN_INTO(element))
12191   {
12192     if (element == EL_ACID && dx == 0 && dy == 1)
12193     {
12194       SplashAcid(new_jx, new_jy);
12195       Feld[jx][jy] = EL_PLAYER_1;
12196       InitMovingField(jx, jy, MV_DOWN);
12197       Store[jx][jy] = EL_ACID;
12198       ContinueMoving(jx, jy);
12199       BuryPlayer(player);
12200     }
12201     else
12202       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12203
12204     return MP_MOVING;
12205   }
12206 #endif
12207
12208   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12209   if (can_move != MP_MOVING)
12210     return can_move;
12211
12212   /* check if DigField() has caused relocation of the player */
12213   if (player->jx != jx || player->jy != jy)
12214     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12215
12216   StorePlayer[jx][jy] = 0;
12217   player->last_jx = jx;
12218   player->last_jy = jy;
12219   player->jx = new_jx;
12220   player->jy = new_jy;
12221   StorePlayer[new_jx][new_jy] = player->element_nr;
12222
12223   if (player->move_delay_value_next != -1)
12224   {
12225     player->move_delay_value = player->move_delay_value_next;
12226     player->move_delay_value_next = -1;
12227   }
12228
12229   player->MovPos =
12230     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12231
12232   player->step_counter++;
12233
12234   PlayerVisit[jx][jy] = FrameCounter;
12235
12236 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12237   player->is_moving = TRUE;
12238 #endif
12239
12240 #if 1
12241   /* should better be called in MovePlayer(), but this breaks some tapes */
12242   ScrollPlayer(player, SCROLL_INIT);
12243 #endif
12244
12245   return MP_MOVING;
12246 }
12247
12248 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12249 {
12250   int jx = player->jx, jy = player->jy;
12251   int old_jx = jx, old_jy = jy;
12252   int moved = MP_NO_ACTION;
12253
12254   if (!player->active)
12255     return FALSE;
12256
12257   if (!dx && !dy)
12258   {
12259     if (player->MovPos == 0)
12260     {
12261       player->is_moving = FALSE;
12262       player->is_digging = FALSE;
12263       player->is_collecting = FALSE;
12264       player->is_snapping = FALSE;
12265       player->is_pushing = FALSE;
12266     }
12267
12268     return FALSE;
12269   }
12270
12271   if (player->move_delay > 0)
12272     return FALSE;
12273
12274   player->move_delay = -1;              /* set to "uninitialized" value */
12275
12276   /* store if player is automatically moved to next field */
12277   player->is_auto_moving = (player->programmed_action != MV_NONE);
12278
12279   /* remove the last programmed player action */
12280   player->programmed_action = 0;
12281
12282   if (player->MovPos)
12283   {
12284     /* should only happen if pre-1.2 tape recordings are played */
12285     /* this is only for backward compatibility */
12286
12287     int original_move_delay_value = player->move_delay_value;
12288
12289 #if DEBUG
12290     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12291            tape.counter);
12292 #endif
12293
12294     /* scroll remaining steps with finest movement resolution */
12295     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12296
12297     while (player->MovPos)
12298     {
12299       ScrollPlayer(player, SCROLL_GO_ON);
12300       ScrollScreen(NULL, SCROLL_GO_ON);
12301
12302       AdvanceFrameAndPlayerCounters(player->index_nr);
12303
12304       DrawAllPlayers();
12305       BackToFront();
12306     }
12307
12308     player->move_delay_value = original_move_delay_value;
12309   }
12310
12311   player->is_active = FALSE;
12312
12313   if (player->last_move_dir & MV_HORIZONTAL)
12314   {
12315     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12316       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12317   }
12318   else
12319   {
12320     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12321       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12322   }
12323
12324 #if USE_FIXED_BORDER_RUNNING_GFX
12325   if (!moved && !player->is_active)
12326   {
12327     player->is_moving = FALSE;
12328     player->is_digging = FALSE;
12329     player->is_collecting = FALSE;
12330     player->is_snapping = FALSE;
12331     player->is_pushing = FALSE;
12332   }
12333 #endif
12334
12335   jx = player->jx;
12336   jy = player->jy;
12337
12338 #if 1
12339   if (moved & MP_MOVING && !ScreenMovPos &&
12340       (player->index_nr == game.centered_player_nr ||
12341        game.centered_player_nr == -1))
12342 #else
12343   if (moved & MP_MOVING && !ScreenMovPos &&
12344       (player == local_player || !options.network))
12345 #endif
12346   {
12347     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12348     int offset = game.scroll_delay_value;
12349
12350     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12351     {
12352       /* actual player has left the screen -- scroll in that direction */
12353       if (jx != old_jx)         /* player has moved horizontally */
12354         scroll_x += (jx - old_jx);
12355       else                      /* player has moved vertically */
12356         scroll_y += (jy - old_jy);
12357     }
12358     else
12359     {
12360       if (jx != old_jx)         /* player has moved horizontally */
12361       {
12362         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12363             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12364           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12365
12366         /* don't scroll over playfield boundaries */
12367         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12368           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12369
12370         /* don't scroll more than one field at a time */
12371         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12372
12373         /* don't scroll against the player's moving direction */
12374         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12375             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12376           scroll_x = old_scroll_x;
12377       }
12378       else                      /* player has moved vertically */
12379       {
12380         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12381             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12382           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12383
12384         /* don't scroll over playfield boundaries */
12385         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12386           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12387
12388         /* don't scroll more than one field at a time */
12389         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12390
12391         /* don't scroll against the player's moving direction */
12392         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12393             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12394           scroll_y = old_scroll_y;
12395       }
12396     }
12397
12398     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12399     {
12400 #if 1
12401       if (!options.network && game.centered_player_nr == -1 &&
12402           !AllPlayersInVisibleScreen())
12403       {
12404         scroll_x = old_scroll_x;
12405         scroll_y = old_scroll_y;
12406       }
12407       else
12408 #else
12409       if (!options.network && !AllPlayersInVisibleScreen())
12410       {
12411         scroll_x = old_scroll_x;
12412         scroll_y = old_scroll_y;
12413       }
12414       else
12415 #endif
12416       {
12417         ScrollScreen(player, SCROLL_INIT);
12418         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12419       }
12420     }
12421   }
12422
12423   player->StepFrame = 0;
12424
12425   if (moved & MP_MOVING)
12426   {
12427     if (old_jx != jx && old_jy == jy)
12428       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12429     else if (old_jx == jx && old_jy != jy)
12430       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12431
12432     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12433
12434     player->last_move_dir = player->MovDir;
12435     player->is_moving = TRUE;
12436     player->is_snapping = FALSE;
12437     player->is_switching = FALSE;
12438     player->is_dropping = FALSE;
12439     player->is_dropping_pressed = FALSE;
12440     player->drop_pressed_delay = 0;
12441
12442 #if 0
12443     /* should better be called here than above, but this breaks some tapes */
12444     ScrollPlayer(player, SCROLL_INIT);
12445 #endif
12446   }
12447   else
12448   {
12449     CheckGravityMovementWhenNotMoving(player);
12450
12451     player->is_moving = FALSE;
12452
12453     /* at this point, the player is allowed to move, but cannot move right now
12454        (e.g. because of something blocking the way) -- ensure that the player
12455        is also allowed to move in the next frame (in old versions before 3.1.1,
12456        the player was forced to wait again for eight frames before next try) */
12457
12458     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12459       player->move_delay = 0;   /* allow direct movement in the next frame */
12460   }
12461
12462   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12463     player->move_delay = player->move_delay_value;
12464
12465   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12466   {
12467     TestIfPlayerTouchesBadThing(jx, jy);
12468     TestIfPlayerTouchesCustomElement(jx, jy);
12469   }
12470
12471   if (!player->active)
12472     RemovePlayer(player);
12473
12474   return moved;
12475 }
12476
12477 void ScrollPlayer(struct PlayerInfo *player, int mode)
12478 {
12479   int jx = player->jx, jy = player->jy;
12480   int last_jx = player->last_jx, last_jy = player->last_jy;
12481   int move_stepsize = TILEX / player->move_delay_value;
12482
12483 #if USE_NEW_PLAYER_SPEED
12484   if (!player->active)
12485     return;
12486
12487   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12488     return;
12489 #else
12490   if (!player->active || player->MovPos == 0)
12491     return;
12492 #endif
12493
12494   if (mode == SCROLL_INIT)
12495   {
12496     player->actual_frame_counter = FrameCounter;
12497     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12498
12499     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12500         Feld[last_jx][last_jy] == EL_EMPTY)
12501     {
12502       int last_field_block_delay = 0;   /* start with no blocking at all */
12503       int block_delay_adjustment = player->block_delay_adjustment;
12504
12505       /* if player blocks last field, add delay for exactly one move */
12506       if (player->block_last_field)
12507       {
12508         last_field_block_delay += player->move_delay_value;
12509
12510         /* when blocking enabled, prevent moving up despite gravity */
12511 #if USE_PLAYER_GRAVITY
12512         if (player->gravity && player->MovDir == MV_UP)
12513           block_delay_adjustment = -1;
12514 #else
12515         if (game.gravity && player->MovDir == MV_UP)
12516           block_delay_adjustment = -1;
12517 #endif
12518       }
12519
12520       /* add block delay adjustment (also possible when not blocking) */
12521       last_field_block_delay += block_delay_adjustment;
12522
12523       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12524       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12525     }
12526
12527 #if USE_NEW_PLAYER_SPEED
12528     if (player->MovPos != 0)    /* player has not yet reached destination */
12529       return;
12530 #else
12531     return;
12532 #endif
12533   }
12534   else if (!FrameReached(&player->actual_frame_counter, 1))
12535     return;
12536
12537 #if USE_NEW_PLAYER_SPEED
12538   if (player->MovPos != 0)
12539   {
12540     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12541     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12542
12543     /* before DrawPlayer() to draw correct player graphic for this case */
12544     if (player->MovPos == 0)
12545       CheckGravityMovement(player);
12546   }
12547 #else
12548   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12549   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12550
12551   /* before DrawPlayer() to draw correct player graphic for this case */
12552   if (player->MovPos == 0)
12553     CheckGravityMovement(player);
12554 #endif
12555
12556   if (player->MovPos == 0)      /* player reached destination field */
12557   {
12558     if (player->move_delay_reset_counter > 0)
12559     {
12560       player->move_delay_reset_counter--;
12561
12562       if (player->move_delay_reset_counter == 0)
12563       {
12564         /* continue with normal speed after quickly moving through gate */
12565         HALVE_PLAYER_SPEED(player);
12566
12567         /* be able to make the next move without delay */
12568         player->move_delay = 0;
12569       }
12570     }
12571
12572     player->last_jx = jx;
12573     player->last_jy = jy;
12574
12575     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12576         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12577         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12578         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12579         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12580         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12581     {
12582       DrawPlayer(player);       /* needed here only to cleanup last field */
12583       RemovePlayer(player);
12584
12585       if (local_player->friends_still_needed == 0 ||
12586           IS_SP_ELEMENT(Feld[jx][jy]))
12587         PlayerWins(player);
12588     }
12589
12590     /* this breaks one level: "machine", level 000 */
12591     {
12592       int move_direction = player->MovDir;
12593       int enter_side = MV_DIR_OPPOSITE(move_direction);
12594       int leave_side = move_direction;
12595       int old_jx = last_jx;
12596       int old_jy = last_jy;
12597       int old_element = Feld[old_jx][old_jy];
12598       int new_element = Feld[jx][jy];
12599
12600       if (IS_CUSTOM_ELEMENT(old_element))
12601         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12602                                    CE_LEFT_BY_PLAYER,
12603                                    player->index_bit, leave_side);
12604
12605       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12606                                           CE_PLAYER_LEAVES_X,
12607                                           player->index_bit, leave_side);
12608
12609       if (IS_CUSTOM_ELEMENT(new_element))
12610         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12611                                    player->index_bit, enter_side);
12612
12613       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12614                                           CE_PLAYER_ENTERS_X,
12615                                           player->index_bit, enter_side);
12616
12617       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12618                                         CE_MOVE_OF_X, move_direction);
12619     }
12620
12621     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12622     {
12623       TestIfPlayerTouchesBadThing(jx, jy);
12624       TestIfPlayerTouchesCustomElement(jx, jy);
12625
12626       /* needed because pushed element has not yet reached its destination,
12627          so it would trigger a change event at its previous field location */
12628       if (!player->is_pushing)
12629         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12630
12631       if (!player->active)
12632         RemovePlayer(player);
12633     }
12634
12635     if (!local_player->LevelSolved && level.use_step_counter)
12636     {
12637       int i;
12638
12639       TimePlayed++;
12640
12641       if (TimeLeft > 0)
12642       {
12643         TimeLeft--;
12644
12645         if (TimeLeft <= 10 && setup.time_limit)
12646           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12647
12648 #if 1
12649         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12650
12651         DisplayGameControlValues();
12652 #else
12653         DrawGameValue_Time(TimeLeft);
12654 #endif
12655
12656         if (!TimeLeft && setup.time_limit)
12657           for (i = 0; i < MAX_PLAYERS; i++)
12658             KillPlayer(&stored_player[i]);
12659       }
12660 #if 1
12661       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12662       {
12663         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12664
12665         DisplayGameControlValues();
12666       }
12667 #else
12668       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12669         DrawGameValue_Time(TimePlayed);
12670 #endif
12671     }
12672
12673     if (tape.single_step && tape.recording && !tape.pausing &&
12674         !player->programmed_action)
12675       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12676   }
12677 }
12678
12679 void ScrollScreen(struct PlayerInfo *player, int mode)
12680 {
12681   static unsigned long screen_frame_counter = 0;
12682
12683   if (mode == SCROLL_INIT)
12684   {
12685     /* set scrolling step size according to actual player's moving speed */
12686     ScrollStepSize = TILEX / player->move_delay_value;
12687
12688     screen_frame_counter = FrameCounter;
12689     ScreenMovDir = player->MovDir;
12690     ScreenMovPos = player->MovPos;
12691     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12692     return;
12693   }
12694   else if (!FrameReached(&screen_frame_counter, 1))
12695     return;
12696
12697   if (ScreenMovPos)
12698   {
12699     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12700     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12701     redraw_mask |= REDRAW_FIELD;
12702   }
12703   else
12704     ScreenMovDir = MV_NONE;
12705 }
12706
12707 void TestIfPlayerTouchesCustomElement(int x, int y)
12708 {
12709   static int xy[4][2] =
12710   {
12711     { 0, -1 },
12712     { -1, 0 },
12713     { +1, 0 },
12714     { 0, +1 }
12715   };
12716   static int trigger_sides[4][2] =
12717   {
12718     /* center side       border side */
12719     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12720     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12721     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12722     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12723   };
12724   static int touch_dir[4] =
12725   {
12726     MV_LEFT | MV_RIGHT,
12727     MV_UP   | MV_DOWN,
12728     MV_UP   | MV_DOWN,
12729     MV_LEFT | MV_RIGHT
12730   };
12731   int center_element = Feld[x][y];      /* should always be non-moving! */
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 center_side = trigger_sides[i][0];
12739     int border_side = trigger_sides[i][1];
12740     int border_element;
12741
12742     if (!IN_LEV_FIELD(xx, yy))
12743       continue;
12744
12745     if (IS_PLAYER(x, y))
12746     {
12747       struct PlayerInfo *player = PLAYERINFO(x, y);
12748
12749       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12750         border_element = Feld[xx][yy];          /* may be moving! */
12751       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12752         border_element = Feld[xx][yy];
12753       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12754         border_element = MovingOrBlocked2Element(xx, yy);
12755       else
12756         continue;               /* center and border element do not touch */
12757
12758       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12759                                  player->index_bit, border_side);
12760       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12761                                           CE_PLAYER_TOUCHES_X,
12762                                           player->index_bit, border_side);
12763     }
12764     else if (IS_PLAYER(xx, yy))
12765     {
12766       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12767
12768       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12769       {
12770         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12771           continue;             /* center and border element do not touch */
12772       }
12773
12774       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12775                                  player->index_bit, center_side);
12776       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12777                                           CE_PLAYER_TOUCHES_X,
12778                                           player->index_bit, center_side);
12779       break;
12780     }
12781   }
12782 }
12783
12784 #if USE_ELEMENT_TOUCHING_BUGFIX
12785
12786 void TestIfElementTouchesCustomElement(int x, int y)
12787 {
12788   static int xy[4][2] =
12789   {
12790     { 0, -1 },
12791     { -1, 0 },
12792     { +1, 0 },
12793     { 0, +1 }
12794   };
12795   static int trigger_sides[4][2] =
12796   {
12797     /* center side      border side */
12798     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12799     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12800     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12801     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12802   };
12803   static int touch_dir[4] =
12804   {
12805     MV_LEFT | MV_RIGHT,
12806     MV_UP   | MV_DOWN,
12807     MV_UP   | MV_DOWN,
12808     MV_LEFT | MV_RIGHT
12809   };
12810   boolean change_center_element = FALSE;
12811   int center_element = Feld[x][y];      /* should always be non-moving! */
12812   int border_element_old[NUM_DIRECTIONS];
12813   int i;
12814
12815   for (i = 0; i < NUM_DIRECTIONS; i++)
12816   {
12817     int xx = x + xy[i][0];
12818     int yy = y + xy[i][1];
12819     int border_element;
12820
12821     border_element_old[i] = -1;
12822
12823     if (!IN_LEV_FIELD(xx, yy))
12824       continue;
12825
12826     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12827       border_element = Feld[xx][yy];    /* may be moving! */
12828     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12829       border_element = Feld[xx][yy];
12830     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12831       border_element = MovingOrBlocked2Element(xx, yy);
12832     else
12833       continue;                 /* center and border element do not touch */
12834
12835     border_element_old[i] = border_element;
12836   }
12837
12838   for (i = 0; i < NUM_DIRECTIONS; i++)
12839   {
12840     int xx = x + xy[i][0];
12841     int yy = y + xy[i][1];
12842     int center_side = trigger_sides[i][0];
12843     int border_element = border_element_old[i];
12844
12845     if (border_element == -1)
12846       continue;
12847
12848     /* check for change of border element */
12849     CheckElementChangeBySide(xx, yy, border_element, center_element,
12850                              CE_TOUCHING_X, center_side);
12851   }
12852
12853   for (i = 0; i < NUM_DIRECTIONS; i++)
12854   {
12855     int border_side = trigger_sides[i][1];
12856     int border_element = border_element_old[i];
12857
12858     if (border_element == -1)
12859       continue;
12860
12861     /* check for change of center element (but change it only once) */
12862     if (!change_center_element)
12863       change_center_element =
12864         CheckElementChangeBySide(x, y, center_element, border_element,
12865                                  CE_TOUCHING_X, border_side);
12866   }
12867 }
12868
12869 #else
12870
12871 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12872 {
12873   static int xy[4][2] =
12874   {
12875     { 0, -1 },
12876     { -1, 0 },
12877     { +1, 0 },
12878     { 0, +1 }
12879   };
12880   static int trigger_sides[4][2] =
12881   {
12882     /* center side      border side */
12883     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12884     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12885     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12886     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12887   };
12888   static int touch_dir[4] =
12889   {
12890     MV_LEFT | MV_RIGHT,
12891     MV_UP   | MV_DOWN,
12892     MV_UP   | MV_DOWN,
12893     MV_LEFT | MV_RIGHT
12894   };
12895   boolean change_center_element = FALSE;
12896   int center_element = Feld[x][y];      /* should always be non-moving! */
12897   int i;
12898
12899   for (i = 0; i < NUM_DIRECTIONS; i++)
12900   {
12901     int xx = x + xy[i][0];
12902     int yy = y + xy[i][1];
12903     int center_side = trigger_sides[i][0];
12904     int border_side = trigger_sides[i][1];
12905     int border_element;
12906
12907     if (!IN_LEV_FIELD(xx, yy))
12908       continue;
12909
12910     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12911       border_element = Feld[xx][yy];    /* may be moving! */
12912     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12913       border_element = Feld[xx][yy];
12914     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12915       border_element = MovingOrBlocked2Element(xx, yy);
12916     else
12917       continue;                 /* center and border element do not touch */
12918
12919     /* check for change of center element (but change it only once) */
12920     if (!change_center_element)
12921       change_center_element =
12922         CheckElementChangeBySide(x, y, center_element, border_element,
12923                                  CE_TOUCHING_X, border_side);
12924
12925     /* check for change of border element */
12926     CheckElementChangeBySide(xx, yy, border_element, center_element,
12927                              CE_TOUCHING_X, center_side);
12928   }
12929 }
12930
12931 #endif
12932
12933 void TestIfElementHitsCustomElement(int x, int y, int direction)
12934 {
12935   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12936   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12937   int hitx = x + dx, hity = y + dy;
12938   int hitting_element = Feld[x][y];
12939   int touched_element;
12940
12941   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12942     return;
12943
12944   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12945                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12946
12947   if (IN_LEV_FIELD(hitx, hity))
12948   {
12949     int opposite_direction = MV_DIR_OPPOSITE(direction);
12950     int hitting_side = direction;
12951     int touched_side = opposite_direction;
12952     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12953                           MovDir[hitx][hity] != direction ||
12954                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12955
12956     object_hit = TRUE;
12957
12958     if (object_hit)
12959     {
12960       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12961                                CE_HITTING_X, touched_side);
12962
12963       CheckElementChangeBySide(hitx, hity, touched_element,
12964                                hitting_element, CE_HIT_BY_X, hitting_side);
12965
12966       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12967                                CE_HIT_BY_SOMETHING, opposite_direction);
12968     }
12969   }
12970
12971   /* "hitting something" is also true when hitting the playfield border */
12972   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12973                            CE_HITTING_SOMETHING, direction);
12974 }
12975
12976 #if 0
12977 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12978 {
12979   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12980   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12981   int hitx = x + dx, hity = y + dy;
12982   int hitting_element = Feld[x][y];
12983   int touched_element;
12984 #if 0
12985   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12986                         !IS_FREE(hitx, hity) &&
12987                         (!IS_MOVING(hitx, hity) ||
12988                          MovDir[hitx][hity] != direction ||
12989                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12990 #endif
12991
12992   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12993     return;
12994
12995 #if 0
12996   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12997     return;
12998 #endif
12999
13000   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13001                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13002
13003   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13004                            EP_CAN_SMASH_EVERYTHING, direction);
13005
13006   if (IN_LEV_FIELD(hitx, hity))
13007   {
13008     int opposite_direction = MV_DIR_OPPOSITE(direction);
13009     int hitting_side = direction;
13010     int touched_side = opposite_direction;
13011 #if 0
13012     int touched_element = MovingOrBlocked2Element(hitx, hity);
13013 #endif
13014 #if 1
13015     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13016                           MovDir[hitx][hity] != direction ||
13017                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13018
13019     object_hit = TRUE;
13020 #endif
13021
13022     if (object_hit)
13023     {
13024       int i;
13025
13026       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13027                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13028
13029       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13030                                CE_OTHER_IS_SMASHING, touched_side);
13031
13032       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13033                                CE_OTHER_GETS_SMASHED, hitting_side);
13034     }
13035   }
13036 }
13037 #endif
13038
13039 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13040 {
13041   int i, kill_x = -1, kill_y = -1;
13042
13043   int bad_element = -1;
13044   static int test_xy[4][2] =
13045   {
13046     { 0, -1 },
13047     { -1, 0 },
13048     { +1, 0 },
13049     { 0, +1 }
13050   };
13051   static int test_dir[4] =
13052   {
13053     MV_UP,
13054     MV_LEFT,
13055     MV_RIGHT,
13056     MV_DOWN
13057   };
13058
13059   for (i = 0; i < NUM_DIRECTIONS; i++)
13060   {
13061     int test_x, test_y, test_move_dir, test_element;
13062
13063     test_x = good_x + test_xy[i][0];
13064     test_y = good_y + test_xy[i][1];
13065
13066     if (!IN_LEV_FIELD(test_x, test_y))
13067       continue;
13068
13069     test_move_dir =
13070       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13071
13072     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13073
13074     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13075        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13076     */
13077     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13078         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13079     {
13080       kill_x = test_x;
13081       kill_y = test_y;
13082       bad_element = test_element;
13083
13084       break;
13085     }
13086   }
13087
13088   if (kill_x != -1 || kill_y != -1)
13089   {
13090     if (IS_PLAYER(good_x, good_y))
13091     {
13092       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13093
13094       if (player->shield_deadly_time_left > 0 &&
13095           !IS_INDESTRUCTIBLE(bad_element))
13096         Bang(kill_x, kill_y);
13097       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13098         KillPlayer(player);
13099     }
13100     else
13101       Bang(good_x, good_y);
13102   }
13103 }
13104
13105 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13106 {
13107   int i, kill_x = -1, kill_y = -1;
13108   int bad_element = Feld[bad_x][bad_y];
13109   static int test_xy[4][2] =
13110   {
13111     { 0, -1 },
13112     { -1, 0 },
13113     { +1, 0 },
13114     { 0, +1 }
13115   };
13116   static int touch_dir[4] =
13117   {
13118     MV_LEFT | MV_RIGHT,
13119     MV_UP   | MV_DOWN,
13120     MV_UP   | MV_DOWN,
13121     MV_LEFT | MV_RIGHT
13122   };
13123   static int test_dir[4] =
13124   {
13125     MV_UP,
13126     MV_LEFT,
13127     MV_RIGHT,
13128     MV_DOWN
13129   };
13130
13131   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13132     return;
13133
13134   for (i = 0; i < NUM_DIRECTIONS; i++)
13135   {
13136     int test_x, test_y, test_move_dir, test_element;
13137
13138     test_x = bad_x + test_xy[i][0];
13139     test_y = bad_y + test_xy[i][1];
13140     if (!IN_LEV_FIELD(test_x, test_y))
13141       continue;
13142
13143     test_move_dir =
13144       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13145
13146     test_element = Feld[test_x][test_y];
13147
13148     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13149        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13150     */
13151     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13152         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13153     {
13154       /* good thing is player or penguin that does not move away */
13155       if (IS_PLAYER(test_x, test_y))
13156       {
13157         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13158
13159         if (bad_element == EL_ROBOT && player->is_moving)
13160           continue;     /* robot does not kill player if he is moving */
13161
13162         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13163         {
13164           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13165             continue;           /* center and border element do not touch */
13166         }
13167
13168         kill_x = test_x;
13169         kill_y = test_y;
13170         break;
13171       }
13172       else if (test_element == EL_PENGUIN)
13173       {
13174         kill_x = test_x;
13175         kill_y = test_y;
13176         break;
13177       }
13178     }
13179   }
13180
13181   if (kill_x != -1 || kill_y != -1)
13182   {
13183     if (IS_PLAYER(kill_x, kill_y))
13184     {
13185       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13186
13187       if (player->shield_deadly_time_left > 0 &&
13188           !IS_INDESTRUCTIBLE(bad_element))
13189         Bang(bad_x, bad_y);
13190       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13191         KillPlayer(player);
13192     }
13193     else
13194       Bang(kill_x, kill_y);
13195   }
13196 }
13197
13198 void TestIfPlayerTouchesBadThing(int x, int y)
13199 {
13200   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13201 }
13202
13203 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13204 {
13205   TestIfGoodThingHitsBadThing(x, y, move_dir);
13206 }
13207
13208 void TestIfBadThingTouchesPlayer(int x, int y)
13209 {
13210   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13211 }
13212
13213 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13214 {
13215   TestIfBadThingHitsGoodThing(x, y, move_dir);
13216 }
13217
13218 void TestIfFriendTouchesBadThing(int x, int y)
13219 {
13220   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13221 }
13222
13223 void TestIfBadThingTouchesFriend(int x, int y)
13224 {
13225   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13226 }
13227
13228 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13229 {
13230   int i, kill_x = bad_x, kill_y = bad_y;
13231   static int xy[4][2] =
13232   {
13233     { 0, -1 },
13234     { -1, 0 },
13235     { +1, 0 },
13236     { 0, +1 }
13237   };
13238
13239   for (i = 0; i < NUM_DIRECTIONS; i++)
13240   {
13241     int x, y, element;
13242
13243     x = bad_x + xy[i][0];
13244     y = bad_y + xy[i][1];
13245     if (!IN_LEV_FIELD(x, y))
13246       continue;
13247
13248     element = Feld[x][y];
13249     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13250         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13251     {
13252       kill_x = x;
13253       kill_y = y;
13254       break;
13255     }
13256   }
13257
13258   if (kill_x != bad_x || kill_y != bad_y)
13259     Bang(bad_x, bad_y);
13260 }
13261
13262 void KillPlayer(struct PlayerInfo *player)
13263 {
13264   int jx = player->jx, jy = player->jy;
13265
13266   if (!player->active)
13267     return;
13268
13269   /* the following code was introduced to prevent an infinite loop when calling
13270      -> Bang()
13271      -> CheckTriggeredElementChangeExt()
13272      -> ExecuteCustomElementAction()
13273      -> KillPlayer()
13274      -> (infinitely repeating the above sequence of function calls)
13275      which occurs when killing the player while having a CE with the setting
13276      "kill player X when explosion of <player X>"; the solution using a new
13277      field "player->killed" was chosen for backwards compatibility, although
13278      clever use of the fields "player->active" etc. would probably also work */
13279 #if 1
13280   if (player->killed)
13281     return;
13282 #endif
13283
13284   player->killed = TRUE;
13285
13286   /* remove accessible field at the player's position */
13287   Feld[jx][jy] = EL_EMPTY;
13288
13289   /* deactivate shield (else Bang()/Explode() would not work right) */
13290   player->shield_normal_time_left = 0;
13291   player->shield_deadly_time_left = 0;
13292
13293   Bang(jx, jy);
13294   BuryPlayer(player);
13295 }
13296
13297 static void KillPlayerUnlessEnemyProtected(int x, int y)
13298 {
13299   if (!PLAYER_ENEMY_PROTECTED(x, y))
13300     KillPlayer(PLAYERINFO(x, y));
13301 }
13302
13303 static void KillPlayerUnlessExplosionProtected(int x, int y)
13304 {
13305   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13306     KillPlayer(PLAYERINFO(x, y));
13307 }
13308
13309 void BuryPlayer(struct PlayerInfo *player)
13310 {
13311   int jx = player->jx, jy = player->jy;
13312
13313   if (!player->active)
13314     return;
13315
13316   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13317   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13318
13319   player->GameOver = TRUE;
13320   RemovePlayer(player);
13321 }
13322
13323 void RemovePlayer(struct PlayerInfo *player)
13324 {
13325   int jx = player->jx, jy = player->jy;
13326   int i, found = FALSE;
13327
13328   player->present = FALSE;
13329   player->active = FALSE;
13330
13331   if (!ExplodeField[jx][jy])
13332     StorePlayer[jx][jy] = 0;
13333
13334   if (player->is_moving)
13335     DrawLevelField(player->last_jx, player->last_jy);
13336
13337   for (i = 0; i < MAX_PLAYERS; i++)
13338     if (stored_player[i].active)
13339       found = TRUE;
13340
13341   if (!found)
13342     AllPlayersGone = TRUE;
13343
13344   ExitX = ZX = jx;
13345   ExitY = ZY = jy;
13346 }
13347
13348 #if USE_NEW_SNAP_DELAY
13349 static void setFieldForSnapping(int x, int y, int element, int direction)
13350 {
13351   struct ElementInfo *ei = &element_info[element];
13352   int direction_bit = MV_DIR_TO_BIT(direction);
13353   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13354   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13355                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13356
13357   Feld[x][y] = EL_ELEMENT_SNAPPING;
13358   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13359
13360   ResetGfxAnimation(x, y);
13361
13362   GfxElement[x][y] = element;
13363   GfxAction[x][y] = action;
13364   GfxDir[x][y] = direction;
13365   GfxFrame[x][y] = -1;
13366 }
13367 #endif
13368
13369 /*
13370   =============================================================================
13371   checkDiagonalPushing()
13372   -----------------------------------------------------------------------------
13373   check if diagonal input device direction results in pushing of object
13374   (by checking if the alternative direction is walkable, diggable, ...)
13375   =============================================================================
13376 */
13377
13378 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13379                                     int x, int y, int real_dx, int real_dy)
13380 {
13381   int jx, jy, dx, dy, xx, yy;
13382
13383   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13384     return TRUE;
13385
13386   /* diagonal direction: check alternative direction */
13387   jx = player->jx;
13388   jy = player->jy;
13389   dx = x - jx;
13390   dy = y - jy;
13391   xx = jx + (dx == 0 ? real_dx : 0);
13392   yy = jy + (dy == 0 ? real_dy : 0);
13393
13394   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13395 }
13396
13397 /*
13398   =============================================================================
13399   DigField()
13400   -----------------------------------------------------------------------------
13401   x, y:                 field next to player (non-diagonal) to try to dig to
13402   real_dx, real_dy:     direction as read from input device (can be diagonal)
13403   =============================================================================
13404 */
13405
13406 int DigField(struct PlayerInfo *player,
13407              int oldx, int oldy, int x, int y,
13408              int real_dx, int real_dy, int mode)
13409 {
13410   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13411   boolean player_was_pushing = player->is_pushing;
13412   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13413   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13414   int jx = oldx, jy = oldy;
13415   int dx = x - jx, dy = y - jy;
13416   int nextx = x + dx, nexty = y + dy;
13417   int move_direction = (dx == -1 ? MV_LEFT  :
13418                         dx == +1 ? MV_RIGHT :
13419                         dy == -1 ? MV_UP    :
13420                         dy == +1 ? MV_DOWN  : MV_NONE);
13421   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13422   int dig_side = MV_DIR_OPPOSITE(move_direction);
13423   int old_element = Feld[jx][jy];
13424 #if USE_FIXED_DONT_RUN_INTO
13425   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13426 #else
13427   int element;
13428 #endif
13429   int collect_count;
13430
13431   if (is_player)                /* function can also be called by EL_PENGUIN */
13432   {
13433     if (player->MovPos == 0)
13434     {
13435       player->is_digging = FALSE;
13436       player->is_collecting = FALSE;
13437     }
13438
13439     if (player->MovPos == 0)    /* last pushing move finished */
13440       player->is_pushing = FALSE;
13441
13442     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13443     {
13444       player->is_switching = FALSE;
13445       player->push_delay = -1;
13446
13447       return MP_NO_ACTION;
13448     }
13449   }
13450
13451 #if !USE_FIXED_DONT_RUN_INTO
13452   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13453     return MP_NO_ACTION;
13454 #endif
13455
13456   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13457     old_element = Back[jx][jy];
13458
13459   /* in case of element dropped at player position, check background */
13460   else if (Back[jx][jy] != EL_EMPTY &&
13461            game.engine_version >= VERSION_IDENT(2,2,0,0))
13462     old_element = Back[jx][jy];
13463
13464   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13465     return MP_NO_ACTION;        /* field has no opening in this direction */
13466
13467   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13468     return MP_NO_ACTION;        /* field has no opening in this direction */
13469
13470 #if USE_FIXED_DONT_RUN_INTO
13471   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13472   {
13473     SplashAcid(x, y);
13474
13475     Feld[jx][jy] = player->artwork_element;
13476     InitMovingField(jx, jy, MV_DOWN);
13477     Store[jx][jy] = EL_ACID;
13478     ContinueMoving(jx, jy);
13479     BuryPlayer(player);
13480
13481     return MP_DONT_RUN_INTO;
13482   }
13483 #endif
13484
13485 #if USE_FIXED_DONT_RUN_INTO
13486   if (player_can_move && DONT_RUN_INTO(element))
13487   {
13488     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13489
13490     return MP_DONT_RUN_INTO;
13491   }
13492 #endif
13493
13494 #if USE_FIXED_DONT_RUN_INTO
13495   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13496     return MP_NO_ACTION;
13497 #endif
13498
13499 #if !USE_FIXED_DONT_RUN_INTO
13500   element = Feld[x][y];
13501 #endif
13502
13503   collect_count = element_info[element].collect_count_initial;
13504
13505   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13506     return MP_NO_ACTION;
13507
13508   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13509     player_can_move = player_can_move_or_snap;
13510
13511   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13512       game.engine_version >= VERSION_IDENT(2,2,0,0))
13513   {
13514     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13515                                player->index_bit, dig_side);
13516     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13517                                         player->index_bit, dig_side);
13518
13519     if (element == EL_DC_LANDMINE)
13520       Bang(x, y);
13521
13522     if (Feld[x][y] != element)          /* field changed by snapping */
13523       return MP_ACTION;
13524
13525     return MP_NO_ACTION;
13526   }
13527
13528 #if USE_PLAYER_GRAVITY
13529   if (player->gravity && is_player && !player->is_auto_moving &&
13530       canFallDown(player) && move_direction != MV_DOWN &&
13531       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13532     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13533 #else
13534   if (game.gravity && is_player && !player->is_auto_moving &&
13535       canFallDown(player) && move_direction != MV_DOWN &&
13536       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13537     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13538 #endif
13539
13540   if (player_can_move &&
13541       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13542   {
13543     int sound_element = SND_ELEMENT(element);
13544     int sound_action = ACTION_WALKING;
13545
13546     if (IS_RND_GATE(element))
13547     {
13548       if (!player->key[RND_GATE_NR(element)])
13549         return MP_NO_ACTION;
13550     }
13551     else if (IS_RND_GATE_GRAY(element))
13552     {
13553       if (!player->key[RND_GATE_GRAY_NR(element)])
13554         return MP_NO_ACTION;
13555     }
13556     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13557     {
13558       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13559         return MP_NO_ACTION;
13560     }
13561     else if (element == EL_EXIT_OPEN ||
13562              element == EL_EM_EXIT_OPEN ||
13563              element == EL_STEEL_EXIT_OPEN ||
13564              element == EL_EM_STEEL_EXIT_OPEN ||
13565              element == EL_SP_EXIT_OPEN ||
13566              element == EL_SP_EXIT_OPENING)
13567     {
13568       sound_action = ACTION_PASSING;    /* player is passing exit */
13569     }
13570     else if (element == EL_EMPTY)
13571     {
13572       sound_action = ACTION_MOVING;             /* nothing to walk on */
13573     }
13574
13575     /* play sound from background or player, whatever is available */
13576     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13577       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13578     else
13579       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13580   }
13581   else if (player_can_move &&
13582            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13583   {
13584     if (!ACCESS_FROM(element, opposite_direction))
13585       return MP_NO_ACTION;      /* field not accessible from this direction */
13586
13587     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13588       return MP_NO_ACTION;
13589
13590     if (IS_EM_GATE(element))
13591     {
13592       if (!player->key[EM_GATE_NR(element)])
13593         return MP_NO_ACTION;
13594     }
13595     else if (IS_EM_GATE_GRAY(element))
13596     {
13597       if (!player->key[EM_GATE_GRAY_NR(element)])
13598         return MP_NO_ACTION;
13599     }
13600     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13601     {
13602       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13603         return MP_NO_ACTION;
13604     }
13605     else if (IS_EMC_GATE(element))
13606     {
13607       if (!player->key[EMC_GATE_NR(element)])
13608         return MP_NO_ACTION;
13609     }
13610     else if (IS_EMC_GATE_GRAY(element))
13611     {
13612       if (!player->key[EMC_GATE_GRAY_NR(element)])
13613         return MP_NO_ACTION;
13614     }
13615     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13616     {
13617       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13618         return MP_NO_ACTION;
13619     }
13620     else if (element == EL_DC_GATE_WHITE ||
13621              element == EL_DC_GATE_WHITE_GRAY ||
13622              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13623     {
13624       if (player->num_white_keys == 0)
13625         return MP_NO_ACTION;
13626
13627       player->num_white_keys--;
13628     }
13629     else if (IS_SP_PORT(element))
13630     {
13631       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13632           element == EL_SP_GRAVITY_PORT_RIGHT ||
13633           element == EL_SP_GRAVITY_PORT_UP ||
13634           element == EL_SP_GRAVITY_PORT_DOWN)
13635 #if USE_PLAYER_GRAVITY
13636         player->gravity = !player->gravity;
13637 #else
13638         game.gravity = !game.gravity;
13639 #endif
13640       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13641                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13642                element == EL_SP_GRAVITY_ON_PORT_UP ||
13643                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13644 #if USE_PLAYER_GRAVITY
13645         player->gravity = TRUE;
13646 #else
13647         game.gravity = TRUE;
13648 #endif
13649       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13650                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13651                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13652                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13653 #if USE_PLAYER_GRAVITY
13654         player->gravity = FALSE;
13655 #else
13656         game.gravity = FALSE;
13657 #endif
13658     }
13659
13660     /* automatically move to the next field with double speed */
13661     player->programmed_action = move_direction;
13662
13663     if (player->move_delay_reset_counter == 0)
13664     {
13665       player->move_delay_reset_counter = 2;     /* two double speed steps */
13666
13667       DOUBLE_PLAYER_SPEED(player);
13668     }
13669
13670     PlayLevelSoundAction(x, y, ACTION_PASSING);
13671   }
13672   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13673   {
13674     RemoveField(x, y);
13675
13676     if (mode != DF_SNAP)
13677     {
13678       GfxElement[x][y] = GFX_ELEMENT(element);
13679       player->is_digging = TRUE;
13680     }
13681
13682     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13683
13684     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13685                                         player->index_bit, dig_side);
13686
13687     if (mode == DF_SNAP)
13688     {
13689 #if USE_NEW_SNAP_DELAY
13690       if (level.block_snap_field)
13691         setFieldForSnapping(x, y, element, move_direction);
13692       else
13693         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13694 #else
13695       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13696 #endif
13697
13698       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13699                                           player->index_bit, dig_side);
13700     }
13701   }
13702   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13703   {
13704     RemoveField(x, y);
13705
13706     if (is_player && mode != DF_SNAP)
13707     {
13708       GfxElement[x][y] = element;
13709       player->is_collecting = TRUE;
13710     }
13711
13712     if (element == EL_SPEED_PILL)
13713     {
13714       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13715     }
13716     else if (element == EL_EXTRA_TIME && level.time > 0)
13717     {
13718       TimeLeft += level.extra_time;
13719
13720 #if 1
13721       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13722
13723       DisplayGameControlValues();
13724 #else
13725       DrawGameValue_Time(TimeLeft);
13726 #endif
13727     }
13728     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13729     {
13730       player->shield_normal_time_left += level.shield_normal_time;
13731       if (element == EL_SHIELD_DEADLY)
13732         player->shield_deadly_time_left += level.shield_deadly_time;
13733     }
13734     else if (element == EL_DYNAMITE ||
13735              element == EL_EM_DYNAMITE ||
13736              element == EL_SP_DISK_RED)
13737     {
13738       if (player->inventory_size < MAX_INVENTORY_SIZE)
13739         player->inventory_element[player->inventory_size++] = element;
13740
13741       DrawGameDoorValues();
13742     }
13743     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13744     {
13745       player->dynabomb_count++;
13746       player->dynabombs_left++;
13747     }
13748     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13749     {
13750       player->dynabomb_size++;
13751     }
13752     else if (element == EL_DYNABOMB_INCREASE_POWER)
13753     {
13754       player->dynabomb_xl = TRUE;
13755     }
13756     else if (IS_KEY(element))
13757     {
13758       player->key[KEY_NR(element)] = TRUE;
13759
13760       DrawGameDoorValues();
13761     }
13762     else if (element == EL_DC_KEY_WHITE)
13763     {
13764       player->num_white_keys++;
13765
13766       /* display white keys? */
13767       /* DrawGameDoorValues(); */
13768     }
13769     else if (IS_ENVELOPE(element))
13770     {
13771       player->show_envelope = element;
13772     }
13773     else if (element == EL_EMC_LENSES)
13774     {
13775       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13776
13777       RedrawAllInvisibleElementsForLenses();
13778     }
13779     else if (element == EL_EMC_MAGNIFIER)
13780     {
13781       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13782
13783       RedrawAllInvisibleElementsForMagnifier();
13784     }
13785     else if (IS_DROPPABLE(element) ||
13786              IS_THROWABLE(element))     /* can be collected and dropped */
13787     {
13788       int i;
13789
13790       if (collect_count == 0)
13791         player->inventory_infinite_element = element;
13792       else
13793         for (i = 0; i < collect_count; i++)
13794           if (player->inventory_size < MAX_INVENTORY_SIZE)
13795             player->inventory_element[player->inventory_size++] = element;
13796
13797       DrawGameDoorValues();
13798     }
13799     else if (collect_count > 0)
13800     {
13801       local_player->gems_still_needed -= collect_count;
13802       if (local_player->gems_still_needed < 0)
13803         local_player->gems_still_needed = 0;
13804
13805 #if 1
13806       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13807
13808       DisplayGameControlValues();
13809 #else
13810       DrawGameValue_Emeralds(local_player->gems_still_needed);
13811 #endif
13812     }
13813
13814     RaiseScoreElement(element);
13815     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13816
13817     if (is_player)
13818       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13819                                           player->index_bit, dig_side);
13820
13821     if (mode == DF_SNAP)
13822     {
13823 #if USE_NEW_SNAP_DELAY
13824       if (level.block_snap_field)
13825         setFieldForSnapping(x, y, element, move_direction);
13826       else
13827         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13828 #else
13829       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13830 #endif
13831
13832       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13833                                           player->index_bit, dig_side);
13834     }
13835   }
13836   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13837   {
13838     if (mode == DF_SNAP && element != EL_BD_ROCK)
13839       return MP_NO_ACTION;
13840
13841     if (CAN_FALL(element) && dy)
13842       return MP_NO_ACTION;
13843
13844     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13845         !(element == EL_SPRING && level.use_spring_bug))
13846       return MP_NO_ACTION;
13847
13848     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13849         ((move_direction & MV_VERTICAL &&
13850           ((element_info[element].move_pattern & MV_LEFT &&
13851             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13852            (element_info[element].move_pattern & MV_RIGHT &&
13853             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13854          (move_direction & MV_HORIZONTAL &&
13855           ((element_info[element].move_pattern & MV_UP &&
13856             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13857            (element_info[element].move_pattern & MV_DOWN &&
13858             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13859       return MP_NO_ACTION;
13860
13861     /* do not push elements already moving away faster than player */
13862     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13863         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13864       return MP_NO_ACTION;
13865
13866     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13867     {
13868       if (player->push_delay_value == -1 || !player_was_pushing)
13869         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13870     }
13871     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13872     {
13873       if (player->push_delay_value == -1)
13874         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13875     }
13876     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13877     {
13878       if (!player->is_pushing)
13879         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13880     }
13881
13882     player->is_pushing = TRUE;
13883     player->is_active = TRUE;
13884
13885     if (!(IN_LEV_FIELD(nextx, nexty) &&
13886           (IS_FREE(nextx, nexty) ||
13887            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13888             IS_SB_ELEMENT(element)))))
13889       return MP_NO_ACTION;
13890
13891     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13892       return MP_NO_ACTION;
13893
13894     if (player->push_delay == -1)       /* new pushing; restart delay */
13895       player->push_delay = 0;
13896
13897     if (player->push_delay < player->push_delay_value &&
13898         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13899         element != EL_SPRING && element != EL_BALLOON)
13900     {
13901       /* make sure that there is no move delay before next try to push */
13902       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13903         player->move_delay = 0;
13904
13905       return MP_NO_ACTION;
13906     }
13907
13908     if (IS_SB_ELEMENT(element))
13909     {
13910       if (element == EL_SOKOBAN_FIELD_FULL)
13911       {
13912         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13913         local_player->sokobanfields_still_needed++;
13914       }
13915
13916       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13917       {
13918         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13919         local_player->sokobanfields_still_needed--;
13920       }
13921
13922       Feld[x][y] = EL_SOKOBAN_OBJECT;
13923
13924       if (Back[x][y] == Back[nextx][nexty])
13925         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13926       else if (Back[x][y] != 0)
13927         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13928                                     ACTION_EMPTYING);
13929       else
13930         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13931                                     ACTION_FILLING);
13932
13933       if (local_player->sokobanfields_still_needed == 0 &&
13934           game.emulation == EMU_SOKOBAN)
13935       {
13936         PlayerWins(player);
13937
13938         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13939       }
13940     }
13941     else
13942       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13943
13944     InitMovingField(x, y, move_direction);
13945     GfxAction[x][y] = ACTION_PUSHING;
13946
13947     if (mode == DF_SNAP)
13948       ContinueMoving(x, y);
13949     else
13950       MovPos[x][y] = (dx != 0 ? dx : dy);
13951
13952     Pushed[x][y] = TRUE;
13953     Pushed[nextx][nexty] = TRUE;
13954
13955     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13956       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13957     else
13958       player->push_delay_value = -1;    /* get new value later */
13959
13960     /* check for element change _after_ element has been pushed */
13961     if (game.use_change_when_pushing_bug)
13962     {
13963       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13964                                  player->index_bit, dig_side);
13965       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13966                                           player->index_bit, dig_side);
13967     }
13968   }
13969   else if (IS_SWITCHABLE(element))
13970   {
13971     if (PLAYER_SWITCHING(player, x, y))
13972     {
13973       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13974                                           player->index_bit, dig_side);
13975
13976       return MP_ACTION;
13977     }
13978
13979     player->is_switching = TRUE;
13980     player->switch_x = x;
13981     player->switch_y = y;
13982
13983     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13984
13985     if (element == EL_ROBOT_WHEEL)
13986     {
13987       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13988       ZX = x;
13989       ZY = y;
13990
13991       game.robot_wheel_active = TRUE;
13992
13993       DrawLevelField(x, y);
13994     }
13995     else if (element == EL_SP_TERMINAL)
13996     {
13997       int xx, yy;
13998
13999       SCAN_PLAYFIELD(xx, yy)
14000       {
14001         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14002           Bang(xx, yy);
14003         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14004           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14005       }
14006     }
14007     else if (IS_BELT_SWITCH(element))
14008     {
14009       ToggleBeltSwitch(x, y);
14010     }
14011     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14012              element == EL_SWITCHGATE_SWITCH_DOWN ||
14013              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14014              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14015     {
14016       ToggleSwitchgateSwitch(x, y);
14017     }
14018     else if (element == EL_LIGHT_SWITCH ||
14019              element == EL_LIGHT_SWITCH_ACTIVE)
14020     {
14021       ToggleLightSwitch(x, y);
14022     }
14023     else if (element == EL_TIMEGATE_SWITCH ||
14024              element == EL_DC_TIMEGATE_SWITCH)
14025     {
14026       ActivateTimegateSwitch(x, y);
14027     }
14028     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14029              element == EL_BALLOON_SWITCH_RIGHT ||
14030              element == EL_BALLOON_SWITCH_UP    ||
14031              element == EL_BALLOON_SWITCH_DOWN  ||
14032              element == EL_BALLOON_SWITCH_NONE  ||
14033              element == EL_BALLOON_SWITCH_ANY)
14034     {
14035       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14036                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14037                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14038                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14039                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14040                              move_direction);
14041     }
14042     else if (element == EL_LAMP)
14043     {
14044       Feld[x][y] = EL_LAMP_ACTIVE;
14045       local_player->lights_still_needed--;
14046
14047       ResetGfxAnimation(x, y);
14048       DrawLevelField(x, y);
14049     }
14050     else if (element == EL_TIME_ORB_FULL)
14051     {
14052       Feld[x][y] = EL_TIME_ORB_EMPTY;
14053
14054       if (level.time > 0 || level.use_time_orb_bug)
14055       {
14056         TimeLeft += level.time_orb_time;
14057
14058 #if 1
14059         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14060
14061         DisplayGameControlValues();
14062 #else
14063         DrawGameValue_Time(TimeLeft);
14064 #endif
14065       }
14066
14067       ResetGfxAnimation(x, y);
14068       DrawLevelField(x, y);
14069     }
14070     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14071              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14072     {
14073       int xx, yy;
14074
14075       game.ball_state = !game.ball_state;
14076
14077       SCAN_PLAYFIELD(xx, yy)
14078       {
14079         int e = Feld[xx][yy];
14080
14081         if (game.ball_state)
14082         {
14083           if (e == EL_EMC_MAGIC_BALL)
14084             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14085           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14086             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14087         }
14088         else
14089         {
14090           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14091             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14092           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14093             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14094         }
14095       }
14096     }
14097
14098     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14099                                         player->index_bit, dig_side);
14100
14101     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14102                                         player->index_bit, dig_side);
14103
14104     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14105                                         player->index_bit, dig_side);
14106
14107     return MP_ACTION;
14108   }
14109   else
14110   {
14111     if (!PLAYER_SWITCHING(player, x, y))
14112     {
14113       player->is_switching = TRUE;
14114       player->switch_x = x;
14115       player->switch_y = y;
14116
14117       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14118                                  player->index_bit, dig_side);
14119       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14120                                           player->index_bit, dig_side);
14121
14122       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14123                                  player->index_bit, dig_side);
14124       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14125                                           player->index_bit, dig_side);
14126     }
14127
14128     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14129                                player->index_bit, dig_side);
14130     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14131                                         player->index_bit, dig_side);
14132
14133     return MP_NO_ACTION;
14134   }
14135
14136   player->push_delay = -1;
14137
14138   if (is_player)                /* function can also be called by EL_PENGUIN */
14139   {
14140     if (Feld[x][y] != element)          /* really digged/collected something */
14141     {
14142       player->is_collecting = !player->is_digging;
14143       player->is_active = TRUE;
14144     }
14145   }
14146
14147   return MP_MOVING;
14148 }
14149
14150 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14151 {
14152   int jx = player->jx, jy = player->jy;
14153   int x = jx + dx, y = jy + dy;
14154   int snap_direction = (dx == -1 ? MV_LEFT  :
14155                         dx == +1 ? MV_RIGHT :
14156                         dy == -1 ? MV_UP    :
14157                         dy == +1 ? MV_DOWN  : MV_NONE);
14158   boolean can_continue_snapping = (level.continuous_snapping &&
14159                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14160
14161   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14162     return FALSE;
14163
14164   if (!player->active || !IN_LEV_FIELD(x, y))
14165     return FALSE;
14166
14167   if (dx && dy)
14168     return FALSE;
14169
14170   if (!dx && !dy)
14171   {
14172     if (player->MovPos == 0)
14173       player->is_pushing = FALSE;
14174
14175     player->is_snapping = FALSE;
14176
14177     if (player->MovPos == 0)
14178     {
14179       player->is_moving = FALSE;
14180       player->is_digging = FALSE;
14181       player->is_collecting = FALSE;
14182     }
14183
14184     return FALSE;
14185   }
14186
14187 #if USE_NEW_CONTINUOUS_SNAPPING
14188   /* prevent snapping with already pressed snap key when not allowed */
14189   if (player->is_snapping && !can_continue_snapping)
14190     return FALSE;
14191 #else
14192   if (player->is_snapping)
14193     return FALSE;
14194 #endif
14195
14196   player->MovDir = snap_direction;
14197
14198   if (player->MovPos == 0)
14199   {
14200     player->is_moving = FALSE;
14201     player->is_digging = FALSE;
14202     player->is_collecting = FALSE;
14203   }
14204
14205   player->is_dropping = FALSE;
14206   player->is_dropping_pressed = FALSE;
14207   player->drop_pressed_delay = 0;
14208
14209   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14210     return FALSE;
14211
14212   player->is_snapping = TRUE;
14213   player->is_active = TRUE;
14214
14215   if (player->MovPos == 0)
14216   {
14217     player->is_moving = FALSE;
14218     player->is_digging = FALSE;
14219     player->is_collecting = FALSE;
14220   }
14221
14222   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14223     DrawLevelField(player->last_jx, player->last_jy);
14224
14225   DrawLevelField(x, y);
14226
14227   return TRUE;
14228 }
14229
14230 boolean DropElement(struct PlayerInfo *player)
14231 {
14232   int old_element, new_element;
14233   int dropx = player->jx, dropy = player->jy;
14234   int drop_direction = player->MovDir;
14235   int drop_side = drop_direction;
14236 #if 1
14237   int drop_element = get_next_dropped_element(player);
14238 #else
14239   int drop_element = (player->inventory_size > 0 ?
14240                       player->inventory_element[player->inventory_size - 1] :
14241                       player->inventory_infinite_element != EL_UNDEFINED ?
14242                       player->inventory_infinite_element :
14243                       player->dynabombs_left > 0 ?
14244                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14245                       EL_UNDEFINED);
14246 #endif
14247
14248   player->is_dropping_pressed = TRUE;
14249
14250   /* do not drop an element on top of another element; when holding drop key
14251      pressed without moving, dropped element must move away before the next
14252      element can be dropped (this is especially important if the next element
14253      is dynamite, which can be placed on background for historical reasons) */
14254   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14255     return MP_ACTION;
14256
14257   if (IS_THROWABLE(drop_element))
14258   {
14259     dropx += GET_DX_FROM_DIR(drop_direction);
14260     dropy += GET_DY_FROM_DIR(drop_direction);
14261
14262     if (!IN_LEV_FIELD(dropx, dropy))
14263       return FALSE;
14264   }
14265
14266   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14267   new_element = drop_element;           /* default: no change when dropping */
14268
14269   /* check if player is active, not moving and ready to drop */
14270   if (!player->active || player->MovPos || player->drop_delay > 0)
14271     return FALSE;
14272
14273   /* check if player has anything that can be dropped */
14274   if (new_element == EL_UNDEFINED)
14275     return FALSE;
14276
14277   /* check if drop key was pressed long enough for EM style dynamite */
14278   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14279     return FALSE;
14280
14281   /* check if anything can be dropped at the current position */
14282   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14283     return FALSE;
14284
14285   /* collected custom elements can only be dropped on empty fields */
14286   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14287     return FALSE;
14288
14289   if (old_element != EL_EMPTY)
14290     Back[dropx][dropy] = old_element;   /* store old element on this field */
14291
14292   ResetGfxAnimation(dropx, dropy);
14293   ResetRandomAnimationValue(dropx, dropy);
14294
14295   if (player->inventory_size > 0 ||
14296       player->inventory_infinite_element != EL_UNDEFINED)
14297   {
14298     if (player->inventory_size > 0)
14299     {
14300       player->inventory_size--;
14301
14302       DrawGameDoorValues();
14303
14304       if (new_element == EL_DYNAMITE)
14305         new_element = EL_DYNAMITE_ACTIVE;
14306       else if (new_element == EL_EM_DYNAMITE)
14307         new_element = EL_EM_DYNAMITE_ACTIVE;
14308       else if (new_element == EL_SP_DISK_RED)
14309         new_element = EL_SP_DISK_RED_ACTIVE;
14310     }
14311
14312     Feld[dropx][dropy] = new_element;
14313
14314     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14315       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14316                           el2img(Feld[dropx][dropy]), 0);
14317
14318     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14319
14320     /* needed if previous element just changed to "empty" in the last frame */
14321     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14322
14323     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14324                                player->index_bit, drop_side);
14325     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14326                                         CE_PLAYER_DROPS_X,
14327                                         player->index_bit, drop_side);
14328
14329     TestIfElementTouchesCustomElement(dropx, dropy);
14330   }
14331   else          /* player is dropping a dyna bomb */
14332   {
14333     player->dynabombs_left--;
14334
14335     Feld[dropx][dropy] = new_element;
14336
14337     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14338       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14339                           el2img(Feld[dropx][dropy]), 0);
14340
14341     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14342   }
14343
14344   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14345     InitField_WithBug1(dropx, dropy, FALSE);
14346
14347   new_element = Feld[dropx][dropy];     /* element might have changed */
14348
14349   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14350       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14351   {
14352     int move_direction, nextx, nexty;
14353
14354     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14355       MovDir[dropx][dropy] = drop_direction;
14356
14357     move_direction = MovDir[dropx][dropy];
14358     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14359     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14360
14361     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14362
14363 #if USE_FIX_IMPACT_COLLISION
14364     /* do not cause impact style collision by dropping elements that can fall */
14365     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14366 #else
14367     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14368 #endif
14369   }
14370
14371   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14372   player->is_dropping = TRUE;
14373
14374   player->drop_pressed_delay = 0;
14375   player->is_dropping_pressed = FALSE;
14376
14377   player->drop_x = dropx;
14378   player->drop_y = dropy;
14379
14380   return TRUE;
14381 }
14382
14383 /* ------------------------------------------------------------------------- */
14384 /* game sound playing functions                                              */
14385 /* ------------------------------------------------------------------------- */
14386
14387 static int *loop_sound_frame = NULL;
14388 static int *loop_sound_volume = NULL;
14389
14390 void InitPlayLevelSound()
14391 {
14392   int num_sounds = getSoundListSize();
14393
14394   checked_free(loop_sound_frame);
14395   checked_free(loop_sound_volume);
14396
14397   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14398   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14399 }
14400
14401 static void PlayLevelSound(int x, int y, int nr)
14402 {
14403   int sx = SCREENX(x), sy = SCREENY(y);
14404   int volume, stereo_position;
14405   int max_distance = 8;
14406   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14407
14408   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14409       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14410     return;
14411
14412   if (!IN_LEV_FIELD(x, y) ||
14413       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14414       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14415     return;
14416
14417   volume = SOUND_MAX_VOLUME;
14418
14419   if (!IN_SCR_FIELD(sx, sy))
14420   {
14421     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14422     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14423
14424     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14425   }
14426
14427   stereo_position = (SOUND_MAX_LEFT +
14428                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14429                      (SCR_FIELDX + 2 * max_distance));
14430
14431   if (IS_LOOP_SOUND(nr))
14432   {
14433     /* This assures that quieter loop sounds do not overwrite louder ones,
14434        while restarting sound volume comparison with each new game frame. */
14435
14436     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14437       return;
14438
14439     loop_sound_volume[nr] = volume;
14440     loop_sound_frame[nr] = FrameCounter;
14441   }
14442
14443   PlaySoundExt(nr, volume, stereo_position, type);
14444 }
14445
14446 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14447 {
14448   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14449                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14450                  y < LEVELY(BY1) ? LEVELY(BY1) :
14451                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14452                  sound_action);
14453 }
14454
14455 static void PlayLevelSoundAction(int x, int y, int action)
14456 {
14457   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14458 }
14459
14460 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14461 {
14462   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14463
14464   if (sound_effect != SND_UNDEFINED)
14465     PlayLevelSound(x, y, sound_effect);
14466 }
14467
14468 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14469                                               int action)
14470 {
14471   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14472
14473   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14474     PlayLevelSound(x, y, sound_effect);
14475 }
14476
14477 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14478 {
14479   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14480
14481   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14482     PlayLevelSound(x, y, sound_effect);
14483 }
14484
14485 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14486 {
14487   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14488
14489   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14490     StopSound(sound_effect);
14491 }
14492
14493 static void PlayLevelMusic()
14494 {
14495   if (levelset.music[level_nr] != MUS_UNDEFINED)
14496     PlayMusic(levelset.music[level_nr]);        /* from config file */
14497   else
14498     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14499 }
14500
14501 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14502 {
14503   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14504   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14505   int x = xx - 1 - offset;
14506   int y = yy - 1 - offset;
14507
14508   switch (sample)
14509   {
14510     case SAMPLE_blank:
14511       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14512       break;
14513
14514     case SAMPLE_roll:
14515       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14516       break;
14517
14518     case SAMPLE_stone:
14519       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14520       break;
14521
14522     case SAMPLE_nut:
14523       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14524       break;
14525
14526     case SAMPLE_crack:
14527       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14528       break;
14529
14530     case SAMPLE_bug:
14531       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14532       break;
14533
14534     case SAMPLE_tank:
14535       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14536       break;
14537
14538     case SAMPLE_android_clone:
14539       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14540       break;
14541
14542     case SAMPLE_android_move:
14543       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14544       break;
14545
14546     case SAMPLE_spring:
14547       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14548       break;
14549
14550     case SAMPLE_slurp:
14551       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14552       break;
14553
14554     case SAMPLE_eater:
14555       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14556       break;
14557
14558     case SAMPLE_eater_eat:
14559       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14560       break;
14561
14562     case SAMPLE_alien:
14563       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14564       break;
14565
14566     case SAMPLE_collect:
14567       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14568       break;
14569
14570     case SAMPLE_diamond:
14571       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14572       break;
14573
14574     case SAMPLE_squash:
14575       /* !!! CHECK THIS !!! */
14576 #if 1
14577       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14578 #else
14579       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14580 #endif
14581       break;
14582
14583     case SAMPLE_wonderfall:
14584       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14585       break;
14586
14587     case SAMPLE_drip:
14588       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14589       break;
14590
14591     case SAMPLE_push:
14592       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14593       break;
14594
14595     case SAMPLE_dirt:
14596       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14597       break;
14598
14599     case SAMPLE_acid:
14600       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14601       break;
14602
14603     case SAMPLE_ball:
14604       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14605       break;
14606
14607     case SAMPLE_grow:
14608       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14609       break;
14610
14611     case SAMPLE_wonder:
14612       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14613       break;
14614
14615     case SAMPLE_door:
14616       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14617       break;
14618
14619     case SAMPLE_exit_open:
14620       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14621       break;
14622
14623     case SAMPLE_exit_leave:
14624       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14625       break;
14626
14627     case SAMPLE_dynamite:
14628       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14629       break;
14630
14631     case SAMPLE_tick:
14632       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14633       break;
14634
14635     case SAMPLE_press:
14636       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14637       break;
14638
14639     case SAMPLE_wheel:
14640       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14641       break;
14642
14643     case SAMPLE_boom:
14644       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14645       break;
14646
14647     case SAMPLE_die:
14648       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14649       break;
14650
14651     case SAMPLE_time:
14652       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14653       break;
14654
14655     default:
14656       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14657       break;
14658   }
14659 }
14660
14661 #if 0
14662 void ChangeTime(int value)
14663 {
14664   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14665
14666   *time += value;
14667
14668   /* EMC game engine uses value from time counter of RND game engine */
14669   level.native_em_level->lev->time = *time;
14670
14671   DrawGameValue_Time(*time);
14672 }
14673
14674 void RaiseScore(int value)
14675 {
14676   /* EMC game engine and RND game engine have separate score counters */
14677   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14678                 &level.native_em_level->lev->score : &local_player->score);
14679
14680   *score += value;
14681
14682   DrawGameValue_Score(*score);
14683 }
14684 #endif
14685
14686 void RaiseScore(int value)
14687 {
14688   local_player->score += value;
14689
14690 #if 1
14691   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14692
14693   DisplayGameControlValues();
14694 #else
14695   DrawGameValue_Score(local_player->score);
14696 #endif
14697 }
14698
14699 void RaiseScoreElement(int element)
14700 {
14701   switch (element)
14702   {
14703     case EL_EMERALD:
14704     case EL_BD_DIAMOND:
14705     case EL_EMERALD_YELLOW:
14706     case EL_EMERALD_RED:
14707     case EL_EMERALD_PURPLE:
14708     case EL_SP_INFOTRON:
14709       RaiseScore(level.score[SC_EMERALD]);
14710       break;
14711     case EL_DIAMOND:
14712       RaiseScore(level.score[SC_DIAMOND]);
14713       break;
14714     case EL_CRYSTAL:
14715       RaiseScore(level.score[SC_CRYSTAL]);
14716       break;
14717     case EL_PEARL:
14718       RaiseScore(level.score[SC_PEARL]);
14719       break;
14720     case EL_BUG:
14721     case EL_BD_BUTTERFLY:
14722     case EL_SP_ELECTRON:
14723       RaiseScore(level.score[SC_BUG]);
14724       break;
14725     case EL_SPACESHIP:
14726     case EL_BD_FIREFLY:
14727     case EL_SP_SNIKSNAK:
14728       RaiseScore(level.score[SC_SPACESHIP]);
14729       break;
14730     case EL_YAMYAM:
14731     case EL_DARK_YAMYAM:
14732       RaiseScore(level.score[SC_YAMYAM]);
14733       break;
14734     case EL_ROBOT:
14735       RaiseScore(level.score[SC_ROBOT]);
14736       break;
14737     case EL_PACMAN:
14738       RaiseScore(level.score[SC_PACMAN]);
14739       break;
14740     case EL_NUT:
14741       RaiseScore(level.score[SC_NUT]);
14742       break;
14743     case EL_DYNAMITE:
14744     case EL_EM_DYNAMITE:
14745     case EL_SP_DISK_RED:
14746     case EL_DYNABOMB_INCREASE_NUMBER:
14747     case EL_DYNABOMB_INCREASE_SIZE:
14748     case EL_DYNABOMB_INCREASE_POWER:
14749       RaiseScore(level.score[SC_DYNAMITE]);
14750       break;
14751     case EL_SHIELD_NORMAL:
14752     case EL_SHIELD_DEADLY:
14753       RaiseScore(level.score[SC_SHIELD]);
14754       break;
14755     case EL_EXTRA_TIME:
14756       RaiseScore(level.extra_time_score);
14757       break;
14758     case EL_KEY_1:
14759     case EL_KEY_2:
14760     case EL_KEY_3:
14761     case EL_KEY_4:
14762     case EL_EM_KEY_1:
14763     case EL_EM_KEY_2:
14764     case EL_EM_KEY_3:
14765     case EL_EM_KEY_4:
14766     case EL_EMC_KEY_5:
14767     case EL_EMC_KEY_6:
14768     case EL_EMC_KEY_7:
14769     case EL_EMC_KEY_8:
14770     case EL_DC_KEY_WHITE:
14771       RaiseScore(level.score[SC_KEY]);
14772       break;
14773     default:
14774       RaiseScore(element_info[element].collect_score);
14775       break;
14776   }
14777 }
14778
14779 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14780 {
14781   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14782   {
14783 #if defined(NETWORK_AVALIABLE)
14784     if (options.network)
14785       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14786     else
14787 #endif
14788     {
14789       if (quick_quit)
14790       {
14791 #if 1
14792
14793 #if 1
14794         FadeSkipNextFadeIn();
14795 #else
14796         fading = fading_none;
14797 #endif
14798
14799 #else
14800         OpenDoor(DOOR_CLOSE_1);
14801 #endif
14802
14803         game_status = GAME_MODE_MAIN;
14804
14805 #if 1
14806         DrawAndFadeInMainMenu(REDRAW_FIELD);
14807 #else
14808         DrawMainMenu();
14809 #endif
14810       }
14811       else
14812       {
14813 #if 0
14814         FadeOut(REDRAW_FIELD);
14815 #endif
14816
14817         game_status = GAME_MODE_MAIN;
14818
14819         DrawAndFadeInMainMenu(REDRAW_FIELD);
14820       }
14821     }
14822   }
14823   else          /* continue playing the game */
14824   {
14825     if (tape.playing && tape.deactivate_display)
14826       TapeDeactivateDisplayOff(TRUE);
14827
14828     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14829
14830     if (tape.playing && tape.deactivate_display)
14831       TapeDeactivateDisplayOn();
14832   }
14833 }
14834
14835 void RequestQuitGame(boolean ask_if_really_quit)
14836 {
14837   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14838   boolean skip_request = AllPlayersGone || quick_quit;
14839
14840   RequestQuitGameExt(skip_request, quick_quit,
14841                      "Do you really want to quit the game ?");
14842 }
14843
14844
14845 /* ------------------------------------------------------------------------- */
14846 /* random generator functions                                                */
14847 /* ------------------------------------------------------------------------- */
14848
14849 unsigned int InitEngineRandom_RND(long seed)
14850 {
14851   game.num_random_calls = 0;
14852
14853 #if 0
14854   unsigned int rnd_seed = InitEngineRandom(seed);
14855
14856   printf("::: START RND: %d\n", rnd_seed);
14857
14858   return rnd_seed;
14859 #else
14860
14861   return InitEngineRandom(seed);
14862
14863 #endif
14864
14865 }
14866
14867 unsigned int RND(int max)
14868 {
14869   if (max > 0)
14870   {
14871     game.num_random_calls++;
14872
14873     return GetEngineRandom(max);
14874   }
14875
14876   return 0;
14877 }
14878
14879
14880 /* ------------------------------------------------------------------------- */
14881 /* game engine snapshot handling functions                                   */
14882 /* ------------------------------------------------------------------------- */
14883
14884 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14885
14886 struct EngineSnapshotInfo
14887 {
14888   /* runtime values for custom element collect score */
14889   int collect_score[NUM_CUSTOM_ELEMENTS];
14890
14891   /* runtime values for group element choice position */
14892   int choice_pos[NUM_GROUP_ELEMENTS];
14893
14894   /* runtime values for belt position animations */
14895   int belt_graphic[4 * NUM_BELT_PARTS];
14896   int belt_anim_mode[4 * NUM_BELT_PARTS];
14897 };
14898
14899 struct EngineSnapshotNodeInfo
14900 {
14901   void *buffer_orig;
14902   void *buffer_copy;
14903   int size;
14904 };
14905
14906 static struct EngineSnapshotInfo engine_snapshot_rnd;
14907 static ListNode *engine_snapshot_list = NULL;
14908 static char *snapshot_level_identifier = NULL;
14909 static int snapshot_level_nr = -1;
14910
14911 void FreeEngineSnapshot()
14912 {
14913   while (engine_snapshot_list != NULL)
14914     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14915                        checked_free);
14916
14917   setString(&snapshot_level_identifier, NULL);
14918   snapshot_level_nr = -1;
14919 }
14920
14921 static void SaveEngineSnapshotValues_RND()
14922 {
14923   static int belt_base_active_element[4] =
14924   {
14925     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14926     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14927     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14928     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14929   };
14930   int i, j;
14931
14932   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14933   {
14934     int element = EL_CUSTOM_START + i;
14935
14936     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14937   }
14938
14939   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14940   {
14941     int element = EL_GROUP_START + i;
14942
14943     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14944   }
14945
14946   for (i = 0; i < 4; i++)
14947   {
14948     for (j = 0; j < NUM_BELT_PARTS; j++)
14949     {
14950       int element = belt_base_active_element[i] + j;
14951       int graphic = el2img(element);
14952       int anim_mode = graphic_info[graphic].anim_mode;
14953
14954       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14955       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14956     }
14957   }
14958 }
14959
14960 static void LoadEngineSnapshotValues_RND()
14961 {
14962   unsigned long num_random_calls = game.num_random_calls;
14963   int i, j;
14964
14965   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14966   {
14967     int element = EL_CUSTOM_START + i;
14968
14969     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14970   }
14971
14972   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14973   {
14974     int element = EL_GROUP_START + i;
14975
14976     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14977   }
14978
14979   for (i = 0; i < 4; i++)
14980   {
14981     for (j = 0; j < NUM_BELT_PARTS; j++)
14982     {
14983       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14984       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14985
14986       graphic_info[graphic].anim_mode = anim_mode;
14987     }
14988   }
14989
14990   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14991   {
14992     InitRND(tape.random_seed);
14993     for (i = 0; i < num_random_calls; i++)
14994       RND(1);
14995   }
14996
14997   if (game.num_random_calls != num_random_calls)
14998   {
14999     Error(ERR_INFO, "number of random calls out of sync");
15000     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15001     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15002     Error(ERR_EXIT, "this should not happen -- please debug");
15003   }
15004 }
15005
15006 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15007 {
15008   struct EngineSnapshotNodeInfo *bi =
15009     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15010
15011   bi->buffer_orig = buffer;
15012   bi->buffer_copy = checked_malloc(size);
15013   bi->size = size;
15014
15015   memcpy(bi->buffer_copy, buffer, size);
15016
15017   addNodeToList(&engine_snapshot_list, NULL, bi);
15018 }
15019
15020 void SaveEngineSnapshot()
15021 {
15022   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15023
15024   if (level_editor_test_game)   /* do not save snapshots from editor */
15025     return;
15026
15027   /* copy some special values to a structure better suited for the snapshot */
15028
15029   SaveEngineSnapshotValues_RND();
15030   SaveEngineSnapshotValues_EM();
15031
15032   /* save values stored in special snapshot structure */
15033
15034   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15035   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15036
15037   /* save further RND engine values */
15038
15039   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15040   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15041   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15042
15043   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15044   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15045   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15046   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15047
15048   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15049   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15050   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15051   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15052   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15053
15054   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15055   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15056   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15057
15058   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15059
15060   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15061
15062   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15063   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15064
15065   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15066   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15067   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15068   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15069   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15070   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15071   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15072   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15073   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15074   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15075   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15076   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15077   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15078   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15079   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15080   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15081   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15082   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15083
15084   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15085   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15086
15087   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15088   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15089   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15090
15091   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15092   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15093
15094   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15095   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15096   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15097   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15098   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15099
15100   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15101   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15102
15103   /* save level identification information */
15104
15105   setString(&snapshot_level_identifier, leveldir_current->identifier);
15106   snapshot_level_nr = level_nr;
15107
15108 #if 0
15109   ListNode *node = engine_snapshot_list;
15110   int num_bytes = 0;
15111
15112   while (node != NULL)
15113   {
15114     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15115
15116     node = node->next;
15117   }
15118
15119   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15120 #endif
15121 }
15122
15123 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15124 {
15125   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15126 }
15127
15128 void LoadEngineSnapshot()
15129 {
15130   ListNode *node = engine_snapshot_list;
15131
15132   if (engine_snapshot_list == NULL)
15133     return;
15134
15135   while (node != NULL)
15136   {
15137     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15138
15139     node = node->next;
15140   }
15141
15142   /* restore special values from snapshot structure */
15143
15144   LoadEngineSnapshotValues_RND();
15145   LoadEngineSnapshotValues_EM();
15146 }
15147
15148 boolean CheckEngineSnapshot()
15149 {
15150   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15151           snapshot_level_nr == level_nr);
15152 }
15153
15154
15155 /* ---------- new game button stuff ---------------------------------------- */
15156
15157 /* graphic position values for game buttons */
15158 #define GAME_BUTTON_XSIZE       30
15159 #define GAME_BUTTON_YSIZE       30
15160 #define GAME_BUTTON_XPOS        5
15161 #define GAME_BUTTON_YPOS        215
15162 #define SOUND_BUTTON_XPOS       5
15163 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15164
15165 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15166 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15167 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15168 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15169 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15170 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15171
15172 static struct
15173 {
15174   int *x, *y;
15175   int gd_x, gd_y;
15176   int gadget_id;
15177   char *infotext;
15178 } gamebutton_info[NUM_GAME_BUTTONS] =
15179 {
15180 #if 1
15181   {
15182     &game.button.stop.x,        &game.button.stop.y,
15183     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15184     GAME_CTRL_ID_STOP,
15185     "stop game"
15186   },
15187   {
15188     &game.button.pause.x,       &game.button.pause.y,
15189     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15190     GAME_CTRL_ID_PAUSE,
15191     "pause game"
15192   },
15193   {
15194     &game.button.play.x,        &game.button.play.y,
15195     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15196     GAME_CTRL_ID_PLAY,
15197     "play game"
15198   },
15199   {
15200     &game.button.sound_music.x, &game.button.sound_music.y,
15201     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15202     SOUND_CTRL_ID_MUSIC,
15203     "background music on/off"
15204   },
15205   {
15206     &game.button.sound_loops.x, &game.button.sound_loops.y,
15207     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15208     SOUND_CTRL_ID_LOOPS,
15209     "sound loops on/off"
15210   },
15211   {
15212     &game.button.sound_simple.x,&game.button.sound_simple.y,
15213     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15214     SOUND_CTRL_ID_SIMPLE,
15215     "normal sounds on/off"
15216   }
15217 #else
15218   {
15219     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15220     GAME_CTRL_ID_STOP,
15221     "stop game"
15222   },
15223   {
15224     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15225     GAME_CTRL_ID_PAUSE,
15226     "pause game"
15227   },
15228   {
15229     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15230     GAME_CTRL_ID_PLAY,
15231     "play game"
15232   },
15233   {
15234     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15235     SOUND_CTRL_ID_MUSIC,
15236     "background music on/off"
15237   },
15238   {
15239     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15240     SOUND_CTRL_ID_LOOPS,
15241     "sound loops on/off"
15242   },
15243   {
15244     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15245     SOUND_CTRL_ID_SIMPLE,
15246     "normal sounds on/off"
15247   }
15248 #endif
15249 };
15250
15251 void CreateGameButtons()
15252 {
15253   int i;
15254
15255   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15256   {
15257     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15258     struct GadgetInfo *gi;
15259     int button_type;
15260     boolean checked;
15261     unsigned long event_mask;
15262     int x, y;
15263     int gd_xoffset, gd_yoffset;
15264     int gd_x1, gd_x2, gd_y1, gd_y2;
15265     int id = i;
15266
15267     x = DX + *gamebutton_info[i].x;
15268     y = DY + *gamebutton_info[i].y;
15269     gd_xoffset = gamebutton_info[i].gd_x;
15270     gd_yoffset = gamebutton_info[i].gd_y;
15271     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15272     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15273
15274     if (id == GAME_CTRL_ID_STOP ||
15275         id == GAME_CTRL_ID_PAUSE ||
15276         id == GAME_CTRL_ID_PLAY)
15277     {
15278       button_type = GD_TYPE_NORMAL_BUTTON;
15279       checked = FALSE;
15280       event_mask = GD_EVENT_RELEASED;
15281       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15282       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15283     }
15284     else
15285     {
15286       button_type = GD_TYPE_CHECK_BUTTON;
15287       checked =
15288         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15289          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15290          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15291       event_mask = GD_EVENT_PRESSED;
15292       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15293       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15294     }
15295
15296     gi = CreateGadget(GDI_CUSTOM_ID, id,
15297                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15298 #if 1
15299                       GDI_X, x,
15300                       GDI_Y, y,
15301 #else
15302                       GDI_X, DX + gd_xoffset,
15303                       GDI_Y, DY + gd_yoffset,
15304 #endif
15305                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15306                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15307                       GDI_TYPE, button_type,
15308                       GDI_STATE, GD_BUTTON_UNPRESSED,
15309                       GDI_CHECKED, checked,
15310                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15311                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15312                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15313                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15314                       GDI_EVENT_MASK, event_mask,
15315                       GDI_CALLBACK_ACTION, HandleGameButtons,
15316                       GDI_END);
15317
15318     if (gi == NULL)
15319       Error(ERR_EXIT, "cannot create gadget");
15320
15321     game_gadget[id] = gi;
15322   }
15323 }
15324
15325 void FreeGameButtons()
15326 {
15327   int i;
15328
15329   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15330     FreeGadget(game_gadget[i]);
15331 }
15332
15333 static void MapGameButtons()
15334 {
15335   int i;
15336
15337   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15338     MapGadget(game_gadget[i]);
15339 }
15340
15341 void UnmapGameButtons()
15342 {
15343   int i;
15344
15345   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15346     UnmapGadget(game_gadget[i]);
15347 }
15348
15349 static void HandleGameButtons(struct GadgetInfo *gi)
15350 {
15351   int id = gi->custom_id;
15352
15353   if (game_status != GAME_MODE_PLAYING)
15354     return;
15355
15356   switch (id)
15357   {
15358     case GAME_CTRL_ID_STOP:
15359       if (tape.playing)
15360         TapeStop();
15361       else
15362         RequestQuitGame(TRUE);
15363       break;
15364
15365     case GAME_CTRL_ID_PAUSE:
15366       if (options.network)
15367       {
15368 #if defined(NETWORK_AVALIABLE)
15369         if (tape.pausing)
15370           SendToServer_ContinuePlaying();
15371         else
15372           SendToServer_PausePlaying();
15373 #endif
15374       }
15375       else
15376         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15377       break;
15378
15379     case GAME_CTRL_ID_PLAY:
15380       if (tape.pausing)
15381       {
15382 #if defined(NETWORK_AVALIABLE)
15383         if (options.network)
15384           SendToServer_ContinuePlaying();
15385         else
15386 #endif
15387         {
15388           tape.pausing = FALSE;
15389           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15390         }
15391       }
15392       break;
15393
15394     case SOUND_CTRL_ID_MUSIC:
15395       if (setup.sound_music)
15396       { 
15397         setup.sound_music = FALSE;
15398         FadeMusic();
15399       }
15400       else if (audio.music_available)
15401       { 
15402         setup.sound = setup.sound_music = TRUE;
15403
15404         SetAudioMode(setup.sound);
15405
15406         PlayLevelMusic();
15407       }
15408       break;
15409
15410     case SOUND_CTRL_ID_LOOPS:
15411       if (setup.sound_loops)
15412         setup.sound_loops = FALSE;
15413       else if (audio.loops_available)
15414       {
15415         setup.sound = setup.sound_loops = TRUE;
15416         SetAudioMode(setup.sound);
15417       }
15418       break;
15419
15420     case SOUND_CTRL_ID_SIMPLE:
15421       if (setup.sound_simple)
15422         setup.sound_simple = FALSE;
15423       else if (audio.sound_available)
15424       {
15425         setup.sound = setup.sound_simple = TRUE;
15426         SetAudioMode(setup.sound);
15427       }
15428       break;
15429
15430     default:
15431       break;
15432   }
15433 }