rnd-20070405-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 = el2img(element);
5469
5470       if (game.belt_dir[i] == MV_LEFT)
5471         graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5472       else
5473         graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5474     }
5475   }
5476
5477   SCAN_PLAYFIELD(x, y)
5478   {
5479     int element = Feld[x][y];
5480
5481     for (i = 0; i < NUM_BELTS; i++)
5482     {
5483       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5484       {
5485         int e_belt_nr = getBeltNrFromBeltElement(element);
5486         int belt_nr = i;
5487
5488         if (e_belt_nr == belt_nr)
5489         {
5490           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5491
5492           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5493         }
5494       }
5495     }
5496   }
5497 }
5498
5499 static void ToggleBeltSwitch(int x, int y)
5500 {
5501   static int belt_base_element[4] =
5502   {
5503     EL_CONVEYOR_BELT_1_LEFT,
5504     EL_CONVEYOR_BELT_2_LEFT,
5505     EL_CONVEYOR_BELT_3_LEFT,
5506     EL_CONVEYOR_BELT_4_LEFT
5507   };
5508   static int belt_base_active_element[4] =
5509   {
5510     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5511     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5512     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5513     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5514   };
5515   static int belt_base_switch_element[4] =
5516   {
5517     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5518     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5519     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5520     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5521   };
5522   static int belt_move_dir[4] =
5523   {
5524     MV_LEFT,
5525     MV_NONE,
5526     MV_RIGHT,
5527     MV_NONE,
5528   };
5529
5530   int element = Feld[x][y];
5531   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5532   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5533   int belt_dir = belt_move_dir[belt_dir_nr];
5534   int xx, yy, i;
5535
5536   if (!IS_BELT_SWITCH(element))
5537     return;
5538
5539   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5540   game.belt_dir[belt_nr] = belt_dir;
5541
5542   if (belt_dir_nr == 3)
5543     belt_dir_nr = 1;
5544
5545   /* set frame order for belt animation graphic according to belt direction */
5546   for (i = 0; i < NUM_BELT_PARTS; i++)
5547   {
5548     int element = belt_base_active_element[belt_nr] + i;
5549     int graphic = el2img(element);
5550
5551     if (belt_dir == MV_LEFT)
5552       graphic_info[graphic].anim_mode &= ~ANIM_REVERSE;
5553     else
5554       graphic_info[graphic].anim_mode |=  ANIM_REVERSE;
5555   }
5556
5557   SCAN_PLAYFIELD(xx, yy)
5558   {
5559     int element = Feld[xx][yy];
5560
5561     if (IS_BELT_SWITCH(element))
5562     {
5563       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5564
5565       if (e_belt_nr == belt_nr)
5566       {
5567         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5568         DrawLevelField(xx, yy);
5569       }
5570     }
5571     else if (IS_BELT(element) && belt_dir != MV_NONE)
5572     {
5573       int e_belt_nr = getBeltNrFromBeltElement(element);
5574
5575       if (e_belt_nr == belt_nr)
5576       {
5577         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5578
5579         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5580         DrawLevelField(xx, yy);
5581       }
5582     }
5583     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5584     {
5585       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5586
5587       if (e_belt_nr == belt_nr)
5588       {
5589         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5590
5591         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5592         DrawLevelField(xx, yy);
5593       }
5594     }
5595   }
5596 }
5597
5598 static void ToggleSwitchgateSwitch(int x, int y)
5599 {
5600   int xx, yy;
5601
5602   game.switchgate_pos = !game.switchgate_pos;
5603
5604   SCAN_PLAYFIELD(xx, yy)
5605   {
5606     int element = Feld[xx][yy];
5607
5608 #if !USE_BOTH_SWITCHGATE_SWITCHES
5609     if (element == EL_SWITCHGATE_SWITCH_UP ||
5610         element == EL_SWITCHGATE_SWITCH_DOWN)
5611     {
5612       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5613       DrawLevelField(xx, yy);
5614     }
5615     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5616              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5617     {
5618       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5619       DrawLevelField(xx, yy);
5620     }
5621 #else
5622     if (element == EL_SWITCHGATE_SWITCH_UP)
5623     {
5624       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5625       DrawLevelField(xx, yy);
5626     }
5627     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5628     {
5629       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5630       DrawLevelField(xx, yy);
5631     }
5632     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5633     {
5634       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5635       DrawLevelField(xx, yy);
5636     }
5637     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5638     {
5639       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5640       DrawLevelField(xx, yy);
5641     }
5642 #endif
5643     else if (element == EL_SWITCHGATE_OPEN ||
5644              element == EL_SWITCHGATE_OPENING)
5645     {
5646       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5647
5648       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5649     }
5650     else if (element == EL_SWITCHGATE_CLOSED ||
5651              element == EL_SWITCHGATE_CLOSING)
5652     {
5653       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5654
5655       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5656     }
5657   }
5658 }
5659
5660 static int getInvisibleActiveFromInvisibleElement(int element)
5661 {
5662   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5663           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5664           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5665           element);
5666 }
5667
5668 static int getInvisibleFromInvisibleActiveElement(int element)
5669 {
5670   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5671           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5672           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5673           element);
5674 }
5675
5676 static void RedrawAllLightSwitchesAndInvisibleElements()
5677 {
5678   int x, y;
5679
5680   SCAN_PLAYFIELD(x, y)
5681   {
5682     int element = Feld[x][y];
5683
5684     if (element == EL_LIGHT_SWITCH &&
5685         game.light_time_left > 0)
5686     {
5687       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5688       DrawLevelField(x, y);
5689     }
5690     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5691              game.light_time_left == 0)
5692     {
5693       Feld[x][y] = EL_LIGHT_SWITCH;
5694       DrawLevelField(x, y);
5695     }
5696     else if (element == EL_EMC_DRIPPER &&
5697              game.light_time_left > 0)
5698     {
5699       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5700       DrawLevelField(x, y);
5701     }
5702     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5703              game.light_time_left == 0)
5704     {
5705       Feld[x][y] = EL_EMC_DRIPPER;
5706       DrawLevelField(x, y);
5707     }
5708     else if (element == EL_INVISIBLE_STEELWALL ||
5709              element == EL_INVISIBLE_WALL ||
5710              element == EL_INVISIBLE_SAND)
5711     {
5712       if (game.light_time_left > 0)
5713         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5714
5715       DrawLevelField(x, y);
5716
5717       /* uncrumble neighbour fields, if needed */
5718       if (element == EL_INVISIBLE_SAND)
5719         DrawLevelFieldCrumbledSandNeighbours(x, y);
5720     }
5721     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5722              element == EL_INVISIBLE_WALL_ACTIVE ||
5723              element == EL_INVISIBLE_SAND_ACTIVE)
5724     {
5725       if (game.light_time_left == 0)
5726         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5727
5728       DrawLevelField(x, y);
5729
5730       /* re-crumble neighbour fields, if needed */
5731       if (element == EL_INVISIBLE_SAND)
5732         DrawLevelFieldCrumbledSandNeighbours(x, y);
5733     }
5734   }
5735 }
5736
5737 static void RedrawAllInvisibleElementsForLenses()
5738 {
5739   int x, y;
5740
5741   SCAN_PLAYFIELD(x, y)
5742   {
5743     int element = Feld[x][y];
5744
5745     if (element == EL_EMC_DRIPPER &&
5746         game.lenses_time_left > 0)
5747     {
5748       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5749       DrawLevelField(x, y);
5750     }
5751     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5752              game.lenses_time_left == 0)
5753     {
5754       Feld[x][y] = EL_EMC_DRIPPER;
5755       DrawLevelField(x, y);
5756     }
5757     else if (element == EL_INVISIBLE_STEELWALL ||
5758              element == EL_INVISIBLE_WALL ||
5759              element == EL_INVISIBLE_SAND)
5760     {
5761       if (game.lenses_time_left > 0)
5762         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5763
5764       DrawLevelField(x, y);
5765
5766       /* uncrumble neighbour fields, if needed */
5767       if (element == EL_INVISIBLE_SAND)
5768         DrawLevelFieldCrumbledSandNeighbours(x, y);
5769     }
5770     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5771              element == EL_INVISIBLE_WALL_ACTIVE ||
5772              element == EL_INVISIBLE_SAND_ACTIVE)
5773     {
5774       if (game.lenses_time_left == 0)
5775         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5776
5777       DrawLevelField(x, y);
5778
5779       /* re-crumble neighbour fields, if needed */
5780       if (element == EL_INVISIBLE_SAND)
5781         DrawLevelFieldCrumbledSandNeighbours(x, y);
5782     }
5783   }
5784 }
5785
5786 static void RedrawAllInvisibleElementsForMagnifier()
5787 {
5788   int x, y;
5789
5790   SCAN_PLAYFIELD(x, y)
5791   {
5792     int element = Feld[x][y];
5793
5794     if (element == EL_EMC_FAKE_GRASS &&
5795         game.magnify_time_left > 0)
5796     {
5797       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5798       DrawLevelField(x, y);
5799     }
5800     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5801              game.magnify_time_left == 0)
5802     {
5803       Feld[x][y] = EL_EMC_FAKE_GRASS;
5804       DrawLevelField(x, y);
5805     }
5806     else if (IS_GATE_GRAY(element) &&
5807              game.magnify_time_left > 0)
5808     {
5809       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5810                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5811                     IS_EM_GATE_GRAY(element) ?
5812                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5813                     IS_EMC_GATE_GRAY(element) ?
5814                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5815                     element);
5816       DrawLevelField(x, y);
5817     }
5818     else if (IS_GATE_GRAY_ACTIVE(element) &&
5819              game.magnify_time_left == 0)
5820     {
5821       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5822                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5823                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5824                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5825                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5826                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5827                     element);
5828       DrawLevelField(x, y);
5829     }
5830   }
5831 }
5832
5833 static void ToggleLightSwitch(int x, int y)
5834 {
5835   int element = Feld[x][y];
5836
5837   game.light_time_left =
5838     (element == EL_LIGHT_SWITCH ?
5839      level.time_light * FRAMES_PER_SECOND : 0);
5840
5841   RedrawAllLightSwitchesAndInvisibleElements();
5842 }
5843
5844 static void ActivateTimegateSwitch(int x, int y)
5845 {
5846   int xx, yy;
5847
5848   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5849
5850   SCAN_PLAYFIELD(xx, yy)
5851   {
5852     int element = Feld[xx][yy];
5853
5854     if (element == EL_TIMEGATE_CLOSED ||
5855         element == EL_TIMEGATE_CLOSING)
5856     {
5857       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5858       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5859     }
5860
5861     /*
5862     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5863     {
5864       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5865       DrawLevelField(xx, yy);
5866     }
5867     */
5868
5869   }
5870
5871 #if 1
5872   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5873                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5874 #else
5875   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
5876 #endif
5877 }
5878
5879 void Impact(int x, int y)
5880 {
5881   boolean last_line = (y == lev_fieldy - 1);
5882   boolean object_hit = FALSE;
5883   boolean impact = (last_line || object_hit);
5884   int element = Feld[x][y];
5885   int smashed = EL_STEELWALL;
5886
5887   if (!last_line)       /* check if element below was hit */
5888   {
5889     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5890       return;
5891
5892     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5893                                          MovDir[x][y + 1] != MV_DOWN ||
5894                                          MovPos[x][y + 1] <= TILEY / 2));
5895
5896     /* do not smash moving elements that left the smashed field in time */
5897     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5898         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5899       object_hit = FALSE;
5900
5901 #if USE_QUICKSAND_IMPACT_BUGFIX
5902     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
5903     {
5904       RemoveMovingField(x, y + 1);
5905       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
5906       Feld[x][y + 2] = EL_ROCK;
5907       DrawLevelField(x, y + 2);
5908
5909       object_hit = TRUE;
5910     }
5911
5912     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
5913     {
5914       RemoveMovingField(x, y + 1);
5915       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
5916       Feld[x][y + 2] = EL_ROCK;
5917       DrawLevelField(x, y + 2);
5918
5919       object_hit = TRUE;
5920     }
5921 #endif
5922
5923     if (object_hit)
5924       smashed = MovingOrBlocked2Element(x, y + 1);
5925
5926     impact = (last_line || object_hit);
5927   }
5928
5929   if (!last_line && smashed == EL_ACID) /* element falls into acid */
5930   {
5931     SplashAcid(x, y + 1);
5932     return;
5933   }
5934
5935   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
5936   /* only reset graphic animation if graphic really changes after impact */
5937   if (impact &&
5938       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
5939   {
5940     ResetGfxAnimation(x, y);
5941     DrawLevelField(x, y);
5942   }
5943
5944   if (impact && CAN_EXPLODE_IMPACT(element))
5945   {
5946     Bang(x, y);
5947     return;
5948   }
5949   else if (impact && element == EL_PEARL &&
5950            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
5951   {
5952     ResetGfxAnimation(x, y);
5953
5954     Feld[x][y] = EL_PEARL_BREAKING;
5955     PlayLevelSound(x, y, SND_PEARL_BREAKING);
5956     return;
5957   }
5958   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
5959   {
5960     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
5961
5962     return;
5963   }
5964
5965   if (impact && element == EL_AMOEBA_DROP)
5966   {
5967     if (object_hit && IS_PLAYER(x, y + 1))
5968       KillPlayerUnlessEnemyProtected(x, y + 1);
5969     else if (object_hit && smashed == EL_PENGUIN)
5970       Bang(x, y + 1);
5971     else
5972     {
5973       Feld[x][y] = EL_AMOEBA_GROWING;
5974       Store[x][y] = EL_AMOEBA_WET;
5975
5976       ResetRandomAnimationValue(x, y);
5977     }
5978     return;
5979   }
5980
5981   if (object_hit)               /* check which object was hit */
5982   {
5983     if ((CAN_PASS_MAGIC_WALL(element) && 
5984          (smashed == EL_MAGIC_WALL ||
5985           smashed == EL_BD_MAGIC_WALL)) ||
5986         (CAN_PASS_DC_MAGIC_WALL(element) &&
5987          smashed == EL_DC_MAGIC_WALL))
5988     {
5989       int xx, yy;
5990       int activated_magic_wall =
5991         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
5992          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
5993          EL_DC_MAGIC_WALL_ACTIVE);
5994
5995       /* activate magic wall / mill */
5996       SCAN_PLAYFIELD(xx, yy)
5997       {
5998         if (Feld[xx][yy] == smashed)
5999           Feld[xx][yy] = activated_magic_wall;
6000       }
6001
6002       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6003       game.magic_wall_active = TRUE;
6004
6005       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6006                             SND_MAGIC_WALL_ACTIVATING :
6007                             smashed == EL_BD_MAGIC_WALL ?
6008                             SND_BD_MAGIC_WALL_ACTIVATING :
6009                             SND_DC_MAGIC_WALL_ACTIVATING));
6010     }
6011
6012     if (IS_PLAYER(x, y + 1))
6013     {
6014       if (CAN_SMASH_PLAYER(element))
6015       {
6016         KillPlayerUnlessEnemyProtected(x, y + 1);
6017         return;
6018       }
6019     }
6020     else if (smashed == EL_PENGUIN)
6021     {
6022       if (CAN_SMASH_PLAYER(element))
6023       {
6024         Bang(x, y + 1);
6025         return;
6026       }
6027     }
6028     else if (element == EL_BD_DIAMOND)
6029     {
6030       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6031       {
6032         Bang(x, y + 1);
6033         return;
6034       }
6035     }
6036     else if (((element == EL_SP_INFOTRON ||
6037                element == EL_SP_ZONK) &&
6038               (smashed == EL_SP_SNIKSNAK ||
6039                smashed == EL_SP_ELECTRON ||
6040                smashed == EL_SP_DISK_ORANGE)) ||
6041              (element == EL_SP_INFOTRON &&
6042               smashed == EL_SP_DISK_YELLOW))
6043     {
6044       Bang(x, y + 1);
6045       return;
6046     }
6047     else if (CAN_SMASH_EVERYTHING(element))
6048     {
6049       if (IS_CLASSIC_ENEMY(smashed) ||
6050           CAN_EXPLODE_SMASHED(smashed))
6051       {
6052         Bang(x, y + 1);
6053         return;
6054       }
6055       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6056       {
6057         if (smashed == EL_LAMP ||
6058             smashed == EL_LAMP_ACTIVE)
6059         {
6060           Bang(x, y + 1);
6061           return;
6062         }
6063         else if (smashed == EL_NUT)
6064         {
6065           Feld[x][y + 1] = EL_NUT_BREAKING;
6066           PlayLevelSound(x, y, SND_NUT_BREAKING);
6067           RaiseScoreElement(EL_NUT);
6068           return;
6069         }
6070         else if (smashed == EL_PEARL)
6071         {
6072           ResetGfxAnimation(x, y);
6073
6074           Feld[x][y + 1] = EL_PEARL_BREAKING;
6075           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6076           return;
6077         }
6078         else if (smashed == EL_DIAMOND)
6079         {
6080           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6081           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6082           return;
6083         }
6084         else if (IS_BELT_SWITCH(smashed))
6085         {
6086           ToggleBeltSwitch(x, y + 1);
6087         }
6088         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6089                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6090                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6091                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6092         {
6093           ToggleSwitchgateSwitch(x, y + 1);
6094         }
6095         else if (smashed == EL_LIGHT_SWITCH ||
6096                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6097         {
6098           ToggleLightSwitch(x, y + 1);
6099         }
6100         else
6101         {
6102 #if 0
6103           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6104 #endif
6105
6106           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6107
6108           CheckElementChangeBySide(x, y + 1, smashed, element,
6109                                    CE_SWITCHED, CH_SIDE_TOP);
6110           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6111                                             CH_SIDE_TOP);
6112         }
6113       }
6114       else
6115       {
6116         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6117       }
6118     }
6119   }
6120
6121   /* play sound of magic wall / mill */
6122   if (!last_line &&
6123       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6124        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6125        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6126   {
6127     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6128       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6129     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6130       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6131     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6132       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6133
6134     return;
6135   }
6136
6137   /* play sound of object that hits the ground */
6138   if (last_line || object_hit)
6139     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6140 }
6141
6142 inline static void TurnRoundExt(int x, int y)
6143 {
6144   static struct
6145   {
6146     int dx, dy;
6147   } move_xy[] =
6148   {
6149     {  0,  0 },
6150     { -1,  0 },
6151     { +1,  0 },
6152     {  0,  0 },
6153     {  0, -1 },
6154     {  0,  0 }, { 0, 0 }, { 0, 0 },
6155     {  0, +1 }
6156   };
6157   static struct
6158   {
6159     int left, right, back;
6160   } turn[] =
6161   {
6162     { 0,        0,              0        },
6163     { MV_DOWN,  MV_UP,          MV_RIGHT },
6164     { MV_UP,    MV_DOWN,        MV_LEFT  },
6165     { 0,        0,              0        },
6166     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6167     { 0,        0,              0        },
6168     { 0,        0,              0        },
6169     { 0,        0,              0        },
6170     { MV_RIGHT, MV_LEFT,        MV_UP    }
6171   };
6172
6173   int element = Feld[x][y];
6174   int move_pattern = element_info[element].move_pattern;
6175
6176   int old_move_dir = MovDir[x][y];
6177   int left_dir  = turn[old_move_dir].left;
6178   int right_dir = turn[old_move_dir].right;
6179   int back_dir  = turn[old_move_dir].back;
6180
6181   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6182   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6183   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6184   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6185
6186   int left_x  = x + left_dx,  left_y  = y + left_dy;
6187   int right_x = x + right_dx, right_y = y + right_dy;
6188   int move_x  = x + move_dx,  move_y  = y + move_dy;
6189
6190   int xx, yy;
6191
6192   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6193   {
6194     TestIfBadThingTouchesOtherBadThing(x, y);
6195
6196     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6197       MovDir[x][y] = right_dir;
6198     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6199       MovDir[x][y] = left_dir;
6200
6201     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6202       MovDelay[x][y] = 9;
6203     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6204       MovDelay[x][y] = 1;
6205   }
6206   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6207   {
6208     TestIfBadThingTouchesOtherBadThing(x, y);
6209
6210     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6211       MovDir[x][y] = left_dir;
6212     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6213       MovDir[x][y] = right_dir;
6214
6215     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6216       MovDelay[x][y] = 9;
6217     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6218       MovDelay[x][y] = 1;
6219   }
6220   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6221   {
6222     TestIfBadThingTouchesOtherBadThing(x, y);
6223
6224     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6225       MovDir[x][y] = left_dir;
6226     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6227       MovDir[x][y] = right_dir;
6228
6229     if (MovDir[x][y] != old_move_dir)
6230       MovDelay[x][y] = 9;
6231   }
6232   else if (element == EL_YAMYAM)
6233   {
6234     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6235     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6236
6237     if (can_turn_left && can_turn_right)
6238       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6239     else if (can_turn_left)
6240       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6241     else if (can_turn_right)
6242       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6243     else
6244       MovDir[x][y] = back_dir;
6245
6246     MovDelay[x][y] = 16 + 16 * RND(3);
6247   }
6248   else if (element == EL_DARK_YAMYAM)
6249   {
6250     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6251                                                          left_x, left_y);
6252     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6253                                                          right_x, right_y);
6254
6255     if (can_turn_left && can_turn_right)
6256       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6257     else if (can_turn_left)
6258       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6259     else if (can_turn_right)
6260       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6261     else
6262       MovDir[x][y] = back_dir;
6263
6264     MovDelay[x][y] = 16 + 16 * RND(3);
6265   }
6266   else if (element == EL_PACMAN)
6267   {
6268     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6269     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6270
6271     if (can_turn_left && can_turn_right)
6272       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6273     else if (can_turn_left)
6274       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6275     else if (can_turn_right)
6276       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6277     else
6278       MovDir[x][y] = back_dir;
6279
6280     MovDelay[x][y] = 6 + RND(40);
6281   }
6282   else if (element == EL_PIG)
6283   {
6284     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6285     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6286     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6287     boolean should_turn_left, should_turn_right, should_move_on;
6288     int rnd_value = 24;
6289     int rnd = RND(rnd_value);
6290
6291     should_turn_left = (can_turn_left &&
6292                         (!can_move_on ||
6293                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6294                                                    y + back_dy + left_dy)));
6295     should_turn_right = (can_turn_right &&
6296                          (!can_move_on ||
6297                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6298                                                     y + back_dy + right_dy)));
6299     should_move_on = (can_move_on &&
6300                       (!can_turn_left ||
6301                        !can_turn_right ||
6302                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6303                                                  y + move_dy + left_dy) ||
6304                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6305                                                  y + move_dy + right_dy)));
6306
6307     if (should_turn_left || should_turn_right || should_move_on)
6308     {
6309       if (should_turn_left && should_turn_right && should_move_on)
6310         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6311                         rnd < 2 * rnd_value / 3 ? right_dir :
6312                         old_move_dir);
6313       else if (should_turn_left && should_turn_right)
6314         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6315       else if (should_turn_left && should_move_on)
6316         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6317       else if (should_turn_right && should_move_on)
6318         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6319       else if (should_turn_left)
6320         MovDir[x][y] = left_dir;
6321       else if (should_turn_right)
6322         MovDir[x][y] = right_dir;
6323       else if (should_move_on)
6324         MovDir[x][y] = old_move_dir;
6325     }
6326     else if (can_move_on && rnd > rnd_value / 8)
6327       MovDir[x][y] = old_move_dir;
6328     else if (can_turn_left && can_turn_right)
6329       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6330     else if (can_turn_left && rnd > rnd_value / 8)
6331       MovDir[x][y] = left_dir;
6332     else if (can_turn_right && rnd > rnd_value/8)
6333       MovDir[x][y] = right_dir;
6334     else
6335       MovDir[x][y] = back_dir;
6336
6337     xx = x + move_xy[MovDir[x][y]].dx;
6338     yy = y + move_xy[MovDir[x][y]].dy;
6339
6340     if (!IN_LEV_FIELD(xx, yy) ||
6341         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6342       MovDir[x][y] = old_move_dir;
6343
6344     MovDelay[x][y] = 0;
6345   }
6346   else if (element == EL_DRAGON)
6347   {
6348     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6349     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6350     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6351     int rnd_value = 24;
6352     int rnd = RND(rnd_value);
6353
6354     if (can_move_on && rnd > rnd_value / 8)
6355       MovDir[x][y] = old_move_dir;
6356     else if (can_turn_left && can_turn_right)
6357       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6358     else if (can_turn_left && rnd > rnd_value / 8)
6359       MovDir[x][y] = left_dir;
6360     else if (can_turn_right && rnd > rnd_value / 8)
6361       MovDir[x][y] = right_dir;
6362     else
6363       MovDir[x][y] = back_dir;
6364
6365     xx = x + move_xy[MovDir[x][y]].dx;
6366     yy = y + move_xy[MovDir[x][y]].dy;
6367
6368     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6369       MovDir[x][y] = old_move_dir;
6370
6371     MovDelay[x][y] = 0;
6372   }
6373   else if (element == EL_MOLE)
6374   {
6375     boolean can_move_on =
6376       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6377                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6378                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6379     if (!can_move_on)
6380     {
6381       boolean can_turn_left =
6382         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6383                               IS_AMOEBOID(Feld[left_x][left_y])));
6384
6385       boolean can_turn_right =
6386         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6387                               IS_AMOEBOID(Feld[right_x][right_y])));
6388
6389       if (can_turn_left && can_turn_right)
6390         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6391       else if (can_turn_left)
6392         MovDir[x][y] = left_dir;
6393       else
6394         MovDir[x][y] = right_dir;
6395     }
6396
6397     if (MovDir[x][y] != old_move_dir)
6398       MovDelay[x][y] = 9;
6399   }
6400   else if (element == EL_BALLOON)
6401   {
6402     MovDir[x][y] = game.wind_direction;
6403     MovDelay[x][y] = 0;
6404   }
6405   else if (element == EL_SPRING)
6406   {
6407 #if USE_NEW_SPRING_BUMPER
6408     if (MovDir[x][y] & MV_HORIZONTAL)
6409     {
6410       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6411           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6412       {
6413         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6414         ResetGfxAnimation(move_x, move_y);
6415         DrawLevelField(move_x, move_y);
6416
6417         MovDir[x][y] = back_dir;
6418       }
6419       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6420                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6421         MovDir[x][y] = MV_NONE;
6422     }
6423 #else
6424     if (MovDir[x][y] & MV_HORIZONTAL &&
6425         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6426          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6427       MovDir[x][y] = MV_NONE;
6428 #endif
6429
6430     MovDelay[x][y] = 0;
6431   }
6432   else if (element == EL_ROBOT ||
6433            element == EL_SATELLITE ||
6434            element == EL_PENGUIN ||
6435            element == EL_EMC_ANDROID)
6436   {
6437     int attr_x = -1, attr_y = -1;
6438
6439     if (AllPlayersGone)
6440     {
6441       attr_x = ExitX;
6442       attr_y = ExitY;
6443     }
6444     else
6445     {
6446       int i;
6447
6448       for (i = 0; i < MAX_PLAYERS; i++)
6449       {
6450         struct PlayerInfo *player = &stored_player[i];
6451         int jx = player->jx, jy = player->jy;
6452
6453         if (!player->active)
6454           continue;
6455
6456         if (attr_x == -1 ||
6457             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6458         {
6459           attr_x = jx;
6460           attr_y = jy;
6461         }
6462       }
6463     }
6464
6465     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6466         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6467          game.engine_version < VERSION_IDENT(3,1,0,0)))
6468     {
6469       attr_x = ZX;
6470       attr_y = ZY;
6471     }
6472
6473     if (element == EL_PENGUIN)
6474     {
6475       int i;
6476       static int xy[4][2] =
6477       {
6478         { 0, -1 },
6479         { -1, 0 },
6480         { +1, 0 },
6481         { 0, +1 }
6482       };
6483
6484       for (i = 0; i < NUM_DIRECTIONS; i++)
6485       {
6486         int ex = x + xy[i][0];
6487         int ey = y + xy[i][1];
6488
6489         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6490                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6491                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6492                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6493         {
6494           attr_x = ex;
6495           attr_y = ey;
6496           break;
6497         }
6498       }
6499     }
6500
6501     MovDir[x][y] = MV_NONE;
6502     if (attr_x < x)
6503       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6504     else if (attr_x > x)
6505       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6506     if (attr_y < y)
6507       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6508     else if (attr_y > y)
6509       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6510
6511     if (element == EL_ROBOT)
6512     {
6513       int newx, newy;
6514
6515       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6516         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6517       Moving2Blocked(x, y, &newx, &newy);
6518
6519       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6520         MovDelay[x][y] = 8 + 8 * !RND(3);
6521       else
6522         MovDelay[x][y] = 16;
6523     }
6524     else if (element == EL_PENGUIN)
6525     {
6526       int newx, newy;
6527
6528       MovDelay[x][y] = 1;
6529
6530       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6531       {
6532         boolean first_horiz = RND(2);
6533         int new_move_dir = MovDir[x][y];
6534
6535         MovDir[x][y] =
6536           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6537         Moving2Blocked(x, y, &newx, &newy);
6538
6539         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6540           return;
6541
6542         MovDir[x][y] =
6543           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6544         Moving2Blocked(x, y, &newx, &newy);
6545
6546         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6547           return;
6548
6549         MovDir[x][y] = old_move_dir;
6550         return;
6551       }
6552     }
6553     else if (element == EL_SATELLITE)
6554     {
6555       int newx, newy;
6556
6557       MovDelay[x][y] = 1;
6558
6559       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6560       {
6561         boolean first_horiz = RND(2);
6562         int new_move_dir = MovDir[x][y];
6563
6564         MovDir[x][y] =
6565           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6566         Moving2Blocked(x, y, &newx, &newy);
6567
6568         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6569           return;
6570
6571         MovDir[x][y] =
6572           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6573         Moving2Blocked(x, y, &newx, &newy);
6574
6575         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6576           return;
6577
6578         MovDir[x][y] = old_move_dir;
6579         return;
6580       }
6581     }
6582     else if (element == EL_EMC_ANDROID)
6583     {
6584       static int check_pos[16] =
6585       {
6586         -1,             /*  0 => (invalid)          */
6587         7,              /*  1 => MV_LEFT            */
6588         3,              /*  2 => MV_RIGHT           */
6589         -1,             /*  3 => (invalid)          */
6590         1,              /*  4 =>            MV_UP   */
6591         0,              /*  5 => MV_LEFT  | MV_UP   */
6592         2,              /*  6 => MV_RIGHT | MV_UP   */
6593         -1,             /*  7 => (invalid)          */
6594         5,              /*  8 =>            MV_DOWN */
6595         6,              /*  9 => MV_LEFT  | MV_DOWN */
6596         4,              /* 10 => MV_RIGHT | MV_DOWN */
6597         -1,             /* 11 => (invalid)          */
6598         -1,             /* 12 => (invalid)          */
6599         -1,             /* 13 => (invalid)          */
6600         -1,             /* 14 => (invalid)          */
6601         -1,             /* 15 => (invalid)          */
6602       };
6603       static struct
6604       {
6605         int dx, dy;
6606         int dir;
6607       } check_xy[8] =
6608       {
6609         { -1, -1,       MV_LEFT  | MV_UP   },
6610         {  0, -1,                  MV_UP   },
6611         { +1, -1,       MV_RIGHT | MV_UP   },
6612         { +1,  0,       MV_RIGHT           },
6613         { +1, +1,       MV_RIGHT | MV_DOWN },
6614         {  0, +1,                  MV_DOWN },
6615         { -1, +1,       MV_LEFT  | MV_DOWN },
6616         { -1,  0,       MV_LEFT            },
6617       };
6618       int start_pos, check_order;
6619       boolean can_clone = FALSE;
6620       int i;
6621
6622       /* check if there is any free field around current position */
6623       for (i = 0; i < 8; i++)
6624       {
6625         int newx = x + check_xy[i].dx;
6626         int newy = y + check_xy[i].dy;
6627
6628         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6629         {
6630           can_clone = TRUE;
6631
6632           break;
6633         }
6634       }
6635
6636       if (can_clone)            /* randomly find an element to clone */
6637       {
6638         can_clone = FALSE;
6639
6640         start_pos = check_pos[RND(8)];
6641         check_order = (RND(2) ? -1 : +1);
6642
6643         for (i = 0; i < 8; i++)
6644         {
6645           int pos_raw = start_pos + i * check_order;
6646           int pos = (pos_raw + 8) % 8;
6647           int newx = x + check_xy[pos].dx;
6648           int newy = y + check_xy[pos].dy;
6649
6650           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6651           {
6652             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6653             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6654
6655             Store[x][y] = Feld[newx][newy];
6656
6657             can_clone = TRUE;
6658
6659             break;
6660           }
6661         }
6662       }
6663
6664       if (can_clone)            /* randomly find a direction to move */
6665       {
6666         can_clone = FALSE;
6667
6668         start_pos = check_pos[RND(8)];
6669         check_order = (RND(2) ? -1 : +1);
6670
6671         for (i = 0; i < 8; i++)
6672         {
6673           int pos_raw = start_pos + i * check_order;
6674           int pos = (pos_raw + 8) % 8;
6675           int newx = x + check_xy[pos].dx;
6676           int newy = y + check_xy[pos].dy;
6677           int new_move_dir = check_xy[pos].dir;
6678
6679           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6680           {
6681             MovDir[x][y] = new_move_dir;
6682             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6683
6684             can_clone = TRUE;
6685
6686             break;
6687           }
6688         }
6689       }
6690
6691       if (can_clone)            /* cloning and moving successful */
6692         return;
6693
6694       /* cannot clone -- try to move towards player */
6695
6696       start_pos = check_pos[MovDir[x][y] & 0x0f];
6697       check_order = (RND(2) ? -1 : +1);
6698
6699       for (i = 0; i < 3; i++)
6700       {
6701         /* first check start_pos, then previous/next or (next/previous) pos */
6702         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6703         int pos = (pos_raw + 8) % 8;
6704         int newx = x + check_xy[pos].dx;
6705         int newy = y + check_xy[pos].dy;
6706         int new_move_dir = check_xy[pos].dir;
6707
6708         if (IS_PLAYER(newx, newy))
6709           break;
6710
6711         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6712         {
6713           MovDir[x][y] = new_move_dir;
6714           MovDelay[x][y] = level.android_move_time * 8 + 1;
6715
6716           break;
6717         }
6718       }
6719     }
6720   }
6721   else if (move_pattern == MV_TURNING_LEFT ||
6722            move_pattern == MV_TURNING_RIGHT ||
6723            move_pattern == MV_TURNING_LEFT_RIGHT ||
6724            move_pattern == MV_TURNING_RIGHT_LEFT ||
6725            move_pattern == MV_TURNING_RANDOM ||
6726            move_pattern == MV_ALL_DIRECTIONS)
6727   {
6728     boolean can_turn_left =
6729       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6730     boolean can_turn_right =
6731       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6732
6733     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6734       return;
6735
6736     if (move_pattern == MV_TURNING_LEFT)
6737       MovDir[x][y] = left_dir;
6738     else if (move_pattern == MV_TURNING_RIGHT)
6739       MovDir[x][y] = right_dir;
6740     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6741       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6742     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6743       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6744     else if (move_pattern == MV_TURNING_RANDOM)
6745       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6746                       can_turn_right && !can_turn_left ? right_dir :
6747                       RND(2) ? left_dir : right_dir);
6748     else if (can_turn_left && can_turn_right)
6749       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6750     else if (can_turn_left)
6751       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6752     else if (can_turn_right)
6753       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6754     else
6755       MovDir[x][y] = back_dir;
6756
6757     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6758   }
6759   else if (move_pattern == MV_HORIZONTAL ||
6760            move_pattern == MV_VERTICAL)
6761   {
6762     if (move_pattern & old_move_dir)
6763       MovDir[x][y] = back_dir;
6764     else if (move_pattern == MV_HORIZONTAL)
6765       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6766     else if (move_pattern == MV_VERTICAL)
6767       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6768
6769     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6770   }
6771   else if (move_pattern & MV_ANY_DIRECTION)
6772   {
6773     MovDir[x][y] = move_pattern;
6774     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6775   }
6776   else if (move_pattern & MV_WIND_DIRECTION)
6777   {
6778     MovDir[x][y] = game.wind_direction;
6779     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6780   }
6781   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6782   {
6783     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6784       MovDir[x][y] = left_dir;
6785     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6786       MovDir[x][y] = right_dir;
6787
6788     if (MovDir[x][y] != old_move_dir)
6789       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6790   }
6791   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6792   {
6793     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6794       MovDir[x][y] = right_dir;
6795     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6796       MovDir[x][y] = left_dir;
6797
6798     if (MovDir[x][y] != old_move_dir)
6799       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6800   }
6801   else if (move_pattern == MV_TOWARDS_PLAYER ||
6802            move_pattern == MV_AWAY_FROM_PLAYER)
6803   {
6804     int attr_x = -1, attr_y = -1;
6805     int newx, newy;
6806     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6807
6808     if (AllPlayersGone)
6809     {
6810       attr_x = ExitX;
6811       attr_y = ExitY;
6812     }
6813     else
6814     {
6815       int i;
6816
6817       for (i = 0; i < MAX_PLAYERS; i++)
6818       {
6819         struct PlayerInfo *player = &stored_player[i];
6820         int jx = player->jx, jy = player->jy;
6821
6822         if (!player->active)
6823           continue;
6824
6825         if (attr_x == -1 ||
6826             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6827         {
6828           attr_x = jx;
6829           attr_y = jy;
6830         }
6831       }
6832     }
6833
6834     MovDir[x][y] = MV_NONE;
6835     if (attr_x < x)
6836       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6837     else if (attr_x > x)
6838       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6839     if (attr_y < y)
6840       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6841     else if (attr_y > y)
6842       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6843
6844     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6845
6846     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6847     {
6848       boolean first_horiz = RND(2);
6849       int new_move_dir = MovDir[x][y];
6850
6851       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6852       {
6853         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6854         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6855
6856         return;
6857       }
6858
6859       MovDir[x][y] =
6860         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6861       Moving2Blocked(x, y, &newx, &newy);
6862
6863       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6864         return;
6865
6866       MovDir[x][y] =
6867         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6868       Moving2Blocked(x, y, &newx, &newy);
6869
6870       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6871         return;
6872
6873       MovDir[x][y] = old_move_dir;
6874     }
6875   }
6876   else if (move_pattern == MV_WHEN_PUSHED ||
6877            move_pattern == MV_WHEN_DROPPED)
6878   {
6879     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6880       MovDir[x][y] = MV_NONE;
6881
6882     MovDelay[x][y] = 0;
6883   }
6884   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6885   {
6886     static int test_xy[7][2] =
6887     {
6888       { 0, -1 },
6889       { -1, 0 },
6890       { +1, 0 },
6891       { 0, +1 },
6892       { 0, -1 },
6893       { -1, 0 },
6894       { +1, 0 },
6895     };
6896     static int test_dir[7] =
6897     {
6898       MV_UP,
6899       MV_LEFT,
6900       MV_RIGHT,
6901       MV_DOWN,
6902       MV_UP,
6903       MV_LEFT,
6904       MV_RIGHT,
6905     };
6906     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6907     int move_preference = -1000000;     /* start with very low preference */
6908     int new_move_dir = MV_NONE;
6909     int start_test = RND(4);
6910     int i;
6911
6912     for (i = 0; i < NUM_DIRECTIONS; i++)
6913     {
6914       int move_dir = test_dir[start_test + i];
6915       int move_dir_preference;
6916
6917       xx = x + test_xy[start_test + i][0];
6918       yy = y + test_xy[start_test + i][1];
6919
6920       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
6921           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
6922       {
6923         new_move_dir = move_dir;
6924
6925         break;
6926       }
6927
6928       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
6929         continue;
6930
6931       move_dir_preference = -1 * RunnerVisit[xx][yy];
6932       if (hunter_mode && PlayerVisit[xx][yy] > 0)
6933         move_dir_preference = PlayerVisit[xx][yy];
6934
6935       if (move_dir_preference > move_preference)
6936       {
6937         /* prefer field that has not been visited for the longest time */
6938         move_preference = move_dir_preference;
6939         new_move_dir = move_dir;
6940       }
6941       else if (move_dir_preference == move_preference &&
6942                move_dir == old_move_dir)
6943       {
6944         /* prefer last direction when all directions are preferred equally */
6945         move_preference = move_dir_preference;
6946         new_move_dir = move_dir;
6947       }
6948     }
6949
6950     MovDir[x][y] = new_move_dir;
6951     if (old_move_dir != new_move_dir)
6952       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6953   }
6954 }
6955
6956 static void TurnRound(int x, int y)
6957 {
6958   int direction = MovDir[x][y];
6959
6960   TurnRoundExt(x, y);
6961
6962   GfxDir[x][y] = MovDir[x][y];
6963
6964   if (direction != MovDir[x][y])
6965     GfxFrame[x][y] = 0;
6966
6967   if (MovDelay[x][y])
6968     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
6969
6970   ResetGfxFrame(x, y, FALSE);
6971 }
6972
6973 static boolean JustBeingPushed(int x, int y)
6974 {
6975   int i;
6976
6977   for (i = 0; i < MAX_PLAYERS; i++)
6978   {
6979     struct PlayerInfo *player = &stored_player[i];
6980
6981     if (player->active && player->is_pushing && player->MovPos)
6982     {
6983       int next_jx = player->jx + (player->jx - player->last_jx);
6984       int next_jy = player->jy + (player->jy - player->last_jy);
6985
6986       if (x == next_jx && y == next_jy)
6987         return TRUE;
6988     }
6989   }
6990
6991   return FALSE;
6992 }
6993
6994 void StartMoving(int x, int y)
6995 {
6996   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
6997   int element = Feld[x][y];
6998
6999   if (Stop[x][y])
7000     return;
7001
7002   if (MovDelay[x][y] == 0)
7003     GfxAction[x][y] = ACTION_DEFAULT;
7004
7005   if (CAN_FALL(element) && y < lev_fieldy - 1)
7006   {
7007     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7008         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7009       if (JustBeingPushed(x, y))
7010         return;
7011
7012     if (element == EL_QUICKSAND_FULL)
7013     {
7014       if (IS_FREE(x, y + 1))
7015       {
7016         InitMovingField(x, y, MV_DOWN);
7017         started_moving = TRUE;
7018
7019         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7020 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7021         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7022           Store[x][y] = EL_ROCK;
7023 #else
7024         Store[x][y] = EL_ROCK;
7025 #endif
7026
7027         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7028       }
7029       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7030       {
7031         if (!MovDelay[x][y])
7032           MovDelay[x][y] = TILEY + 1;
7033
7034         if (MovDelay[x][y])
7035         {
7036           MovDelay[x][y]--;
7037           if (MovDelay[x][y])
7038             return;
7039         }
7040
7041         Feld[x][y] = EL_QUICKSAND_EMPTY;
7042         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7043         Store[x][y + 1] = Store[x][y];
7044         Store[x][y] = 0;
7045
7046         PlayLevelSoundAction(x, y, ACTION_FILLING);
7047       }
7048     }
7049     else if (element == EL_QUICKSAND_FAST_FULL)
7050     {
7051       if (IS_FREE(x, y + 1))
7052       {
7053         InitMovingField(x, y, MV_DOWN);
7054         started_moving = TRUE;
7055
7056         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7057 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7058         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7059           Store[x][y] = EL_ROCK;
7060 #else
7061         Store[x][y] = EL_ROCK;
7062 #endif
7063
7064         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7065       }
7066       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7067       {
7068         if (!MovDelay[x][y])
7069           MovDelay[x][y] = TILEY + 1;
7070
7071         if (MovDelay[x][y])
7072         {
7073           MovDelay[x][y]--;
7074           if (MovDelay[x][y])
7075             return;
7076         }
7077
7078         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7079         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7080         Store[x][y + 1] = Store[x][y];
7081         Store[x][y] = 0;
7082
7083         PlayLevelSoundAction(x, y, ACTION_FILLING);
7084       }
7085     }
7086     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7087              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7088     {
7089       InitMovingField(x, y, MV_DOWN);
7090       started_moving = TRUE;
7091
7092       Feld[x][y] = EL_QUICKSAND_FILLING;
7093       Store[x][y] = element;
7094
7095       PlayLevelSoundAction(x, y, ACTION_FILLING);
7096     }
7097     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7098              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7099     {
7100       InitMovingField(x, y, MV_DOWN);
7101       started_moving = TRUE;
7102
7103       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7104       Store[x][y] = element;
7105
7106       PlayLevelSoundAction(x, y, ACTION_FILLING);
7107     }
7108     else if (element == EL_MAGIC_WALL_FULL)
7109     {
7110       if (IS_FREE(x, y + 1))
7111       {
7112         InitMovingField(x, y, MV_DOWN);
7113         started_moving = TRUE;
7114
7115         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7116         Store[x][y] = EL_CHANGED(Store[x][y]);
7117       }
7118       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7119       {
7120         if (!MovDelay[x][y])
7121           MovDelay[x][y] = TILEY/4 + 1;
7122
7123         if (MovDelay[x][y])
7124         {
7125           MovDelay[x][y]--;
7126           if (MovDelay[x][y])
7127             return;
7128         }
7129
7130         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7131         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7132         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7133         Store[x][y] = 0;
7134       }
7135     }
7136     else if (element == EL_BD_MAGIC_WALL_FULL)
7137     {
7138       if (IS_FREE(x, y + 1))
7139       {
7140         InitMovingField(x, y, MV_DOWN);
7141         started_moving = TRUE;
7142
7143         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7144         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7145       }
7146       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7147       {
7148         if (!MovDelay[x][y])
7149           MovDelay[x][y] = TILEY/4 + 1;
7150
7151         if (MovDelay[x][y])
7152         {
7153           MovDelay[x][y]--;
7154           if (MovDelay[x][y])
7155             return;
7156         }
7157
7158         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7159         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7160         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7161         Store[x][y] = 0;
7162       }
7163     }
7164     else if (element == EL_DC_MAGIC_WALL_FULL)
7165     {
7166       if (IS_FREE(x, y + 1))
7167       {
7168         InitMovingField(x, y, MV_DOWN);
7169         started_moving = TRUE;
7170
7171         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7172         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7173       }
7174       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7175       {
7176         if (!MovDelay[x][y])
7177           MovDelay[x][y] = TILEY/4 + 1;
7178
7179         if (MovDelay[x][y])
7180         {
7181           MovDelay[x][y]--;
7182           if (MovDelay[x][y])
7183             return;
7184         }
7185
7186         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7187         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7188         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7189         Store[x][y] = 0;
7190       }
7191     }
7192     else if ((CAN_PASS_MAGIC_WALL(element) &&
7193               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7194                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7195              (CAN_PASS_DC_MAGIC_WALL(element) &&
7196               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7197
7198     {
7199       InitMovingField(x, y, MV_DOWN);
7200       started_moving = TRUE;
7201
7202       Feld[x][y] =
7203         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7204          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7205          EL_DC_MAGIC_WALL_FILLING);
7206       Store[x][y] = element;
7207     }
7208     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7209     {
7210       SplashAcid(x, y + 1);
7211
7212       InitMovingField(x, y, MV_DOWN);
7213       started_moving = TRUE;
7214
7215       Store[x][y] = EL_ACID;
7216     }
7217     else if (
7218 #if USE_FIX_IMPACT_COLLISION
7219              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7220               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7221 #else
7222              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7223               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7224 #endif
7225              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7226               CAN_FALL(element) && WasJustFalling[x][y] &&
7227               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7228
7229              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7230               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7231               (Feld[x][y + 1] == EL_BLOCKED)))
7232     {
7233       /* this is needed for a special case not covered by calling "Impact()"
7234          from "ContinueMoving()": if an element moves to a tile directly below
7235          another element which was just falling on that tile (which was empty
7236          in the previous frame), the falling element above would just stop
7237          instead of smashing the element below (in previous version, the above
7238          element was just checked for "moving" instead of "falling", resulting
7239          in incorrect smashes caused by horizontal movement of the above
7240          element; also, the case of the player being the element to smash was
7241          simply not covered here... :-/ ) */
7242
7243       CheckCollision[x][y] = 0;
7244       CheckImpact[x][y] = 0;
7245
7246       Impact(x, y);
7247     }
7248     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7249     {
7250       if (MovDir[x][y] == MV_NONE)
7251       {
7252         InitMovingField(x, y, MV_DOWN);
7253         started_moving = TRUE;
7254       }
7255     }
7256     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7257     {
7258       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7259         MovDir[x][y] = MV_DOWN;
7260
7261       InitMovingField(x, y, MV_DOWN);
7262       started_moving = TRUE;
7263     }
7264     else if (element == EL_AMOEBA_DROP)
7265     {
7266       Feld[x][y] = EL_AMOEBA_GROWING;
7267       Store[x][y] = EL_AMOEBA_WET;
7268     }
7269     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7270               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7271              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7272              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7273     {
7274       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7275                                 (IS_FREE(x - 1, y + 1) ||
7276                                  Feld[x - 1][y + 1] == EL_ACID));
7277       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7278                                 (IS_FREE(x + 1, y + 1) ||
7279                                  Feld[x + 1][y + 1] == EL_ACID));
7280       boolean can_fall_any  = (can_fall_left || can_fall_right);
7281       boolean can_fall_both = (can_fall_left && can_fall_right);
7282       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7283
7284 #if USE_NEW_ALL_SLIPPERY
7285       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7286       {
7287         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7288           can_fall_right = FALSE;
7289         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7290           can_fall_left = FALSE;
7291         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7292           can_fall_right = FALSE;
7293         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7294           can_fall_left = FALSE;
7295
7296         can_fall_any  = (can_fall_left || can_fall_right);
7297         can_fall_both = FALSE;
7298       }
7299 #else
7300       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7301       {
7302         if (slippery_type == SLIPPERY_ONLY_LEFT)
7303           can_fall_right = FALSE;
7304         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7305           can_fall_left = FALSE;
7306         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7307           can_fall_right = FALSE;
7308         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7309           can_fall_left = FALSE;
7310
7311         can_fall_any  = (can_fall_left || can_fall_right);
7312         can_fall_both = (can_fall_left && can_fall_right);
7313       }
7314 #endif
7315
7316 #if USE_NEW_ALL_SLIPPERY
7317 #else
7318 #if USE_NEW_SP_SLIPPERY
7319       /* !!! better use the same properties as for custom elements here !!! */
7320       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7321                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7322       {
7323         can_fall_right = FALSE;         /* slip down on left side */
7324         can_fall_both = FALSE;
7325       }
7326 #endif
7327 #endif
7328
7329 #if USE_NEW_ALL_SLIPPERY
7330       if (can_fall_both)
7331       {
7332         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7333           can_fall_right = FALSE;       /* slip down on left side */
7334         else
7335           can_fall_left = !(can_fall_right = RND(2));
7336
7337         can_fall_both = FALSE;
7338       }
7339 #else
7340       if (can_fall_both)
7341       {
7342         if (game.emulation == EMU_BOULDERDASH ||
7343             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7344           can_fall_right = FALSE;       /* slip down on left side */
7345         else
7346           can_fall_left = !(can_fall_right = RND(2));
7347
7348         can_fall_both = FALSE;
7349       }
7350 #endif
7351
7352       if (can_fall_any)
7353       {
7354         /* if not determined otherwise, prefer left side for slipping down */
7355         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7356         started_moving = TRUE;
7357       }
7358     }
7359 #if 0
7360     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7361 #else
7362     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7363 #endif
7364     {
7365       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7366       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7367       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7368       int belt_dir = game.belt_dir[belt_nr];
7369
7370       if ((belt_dir == MV_LEFT  && left_is_free) ||
7371           (belt_dir == MV_RIGHT && right_is_free))
7372       {
7373         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7374
7375         InitMovingField(x, y, belt_dir);
7376         started_moving = TRUE;
7377
7378         Pushed[x][y] = TRUE;
7379         Pushed[nextx][y] = TRUE;
7380
7381         GfxAction[x][y] = ACTION_DEFAULT;
7382       }
7383       else
7384       {
7385         MovDir[x][y] = 0;       /* if element was moving, stop it */
7386       }
7387     }
7388   }
7389
7390   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7391 #if 0
7392   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7393 #else
7394   if (CAN_MOVE(element) && !started_moving)
7395 #endif
7396   {
7397     int move_pattern = element_info[element].move_pattern;
7398     int newx, newy;
7399
7400 #if 0
7401 #if DEBUG
7402     if (MovDir[x][y] == MV_NONE)
7403     {
7404       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7405              x, y, element, element_info[element].token_name);
7406       printf("StartMoving(): This should never happen!\n");
7407     }
7408 #endif
7409 #endif
7410
7411     Moving2Blocked(x, y, &newx, &newy);
7412
7413     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7414       return;
7415
7416     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7417         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7418     {
7419       WasJustMoving[x][y] = 0;
7420       CheckCollision[x][y] = 0;
7421
7422       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7423
7424       if (Feld[x][y] != element)        /* element has changed */
7425         return;
7426     }
7427
7428     if (!MovDelay[x][y])        /* start new movement phase */
7429     {
7430       /* all objects that can change their move direction after each step
7431          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7432
7433       if (element != EL_YAMYAM &&
7434           element != EL_DARK_YAMYAM &&
7435           element != EL_PACMAN &&
7436           !(move_pattern & MV_ANY_DIRECTION) &&
7437           move_pattern != MV_TURNING_LEFT &&
7438           move_pattern != MV_TURNING_RIGHT &&
7439           move_pattern != MV_TURNING_LEFT_RIGHT &&
7440           move_pattern != MV_TURNING_RIGHT_LEFT &&
7441           move_pattern != MV_TURNING_RANDOM)
7442       {
7443         TurnRound(x, y);
7444
7445         if (MovDelay[x][y] && (element == EL_BUG ||
7446                                element == EL_SPACESHIP ||
7447                                element == EL_SP_SNIKSNAK ||
7448                                element == EL_SP_ELECTRON ||
7449                                element == EL_MOLE))
7450           DrawLevelField(x, y);
7451       }
7452     }
7453
7454     if (MovDelay[x][y])         /* wait some time before next movement */
7455     {
7456       MovDelay[x][y]--;
7457
7458       if (element == EL_ROBOT ||
7459           element == EL_YAMYAM ||
7460           element == EL_DARK_YAMYAM)
7461       {
7462         DrawLevelElementAnimationIfNeeded(x, y, element);
7463         PlayLevelSoundAction(x, y, ACTION_WAITING);
7464       }
7465       else if (element == EL_SP_ELECTRON)
7466         DrawLevelElementAnimationIfNeeded(x, y, element);
7467       else if (element == EL_DRAGON)
7468       {
7469         int i;
7470         int dir = MovDir[x][y];
7471         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7472         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7473         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7474                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7475                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7476                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7477         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7478
7479         GfxAction[x][y] = ACTION_ATTACKING;
7480
7481         if (IS_PLAYER(x, y))
7482           DrawPlayerField(x, y);
7483         else
7484           DrawLevelField(x, y);
7485
7486         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7487
7488         for (i = 1; i <= 3; i++)
7489         {
7490           int xx = x + i * dx;
7491           int yy = y + i * dy;
7492           int sx = SCREENX(xx);
7493           int sy = SCREENY(yy);
7494           int flame_graphic = graphic + (i - 1);
7495
7496           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7497             break;
7498
7499           if (MovDelay[x][y])
7500           {
7501             int flamed = MovingOrBlocked2Element(xx, yy);
7502
7503             /* !!! */
7504 #if 0
7505             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7506               Bang(xx, yy);
7507             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7508               RemoveMovingField(xx, yy);
7509             else
7510               RemoveField(xx, yy);
7511 #else
7512             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7513               Bang(xx, yy);
7514             else
7515               RemoveMovingField(xx, yy);
7516 #endif
7517
7518             ChangeDelay[xx][yy] = 0;
7519
7520             Feld[xx][yy] = EL_FLAMES;
7521
7522             if (IN_SCR_FIELD(sx, sy))
7523             {
7524               DrawLevelFieldCrumbledSand(xx, yy);
7525               DrawGraphic(sx, sy, flame_graphic, frame);
7526             }
7527           }
7528           else
7529           {
7530             if (Feld[xx][yy] == EL_FLAMES)
7531               Feld[xx][yy] = EL_EMPTY;
7532             DrawLevelField(xx, yy);
7533           }
7534         }
7535       }
7536
7537       if (MovDelay[x][y])       /* element still has to wait some time */
7538       {
7539         PlayLevelSoundAction(x, y, ACTION_WAITING);
7540
7541         return;
7542       }
7543     }
7544
7545     /* now make next step */
7546
7547     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7548
7549     if (DONT_COLLIDE_WITH(element) &&
7550         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7551         !PLAYER_ENEMY_PROTECTED(newx, newy))
7552     {
7553       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7554
7555       return;
7556     }
7557
7558     else if (CAN_MOVE_INTO_ACID(element) &&
7559              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7560              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7561              (MovDir[x][y] == MV_DOWN ||
7562               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7563     {
7564       SplashAcid(newx, newy);
7565       Store[x][y] = EL_ACID;
7566     }
7567     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7568     {
7569       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7570           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7571           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7572           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7573       {
7574         RemoveField(x, y);
7575         DrawLevelField(x, y);
7576
7577         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7578         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7579           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7580
7581         local_player->friends_still_needed--;
7582         if (!local_player->friends_still_needed &&
7583             !local_player->GameOver && AllPlayersGone)
7584           PlayerWins(local_player);
7585
7586         return;
7587       }
7588       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7589       {
7590         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7591           DrawLevelField(newx, newy);
7592         else
7593           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7594       }
7595       else if (!IS_FREE(newx, newy))
7596       {
7597         GfxAction[x][y] = ACTION_WAITING;
7598
7599         if (IS_PLAYER(x, y))
7600           DrawPlayerField(x, y);
7601         else
7602           DrawLevelField(x, y);
7603
7604         return;
7605       }
7606     }
7607     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7608     {
7609       if (IS_FOOD_PIG(Feld[newx][newy]))
7610       {
7611         if (IS_MOVING(newx, newy))
7612           RemoveMovingField(newx, newy);
7613         else
7614         {
7615           Feld[newx][newy] = EL_EMPTY;
7616           DrawLevelField(newx, newy);
7617         }
7618
7619         PlayLevelSound(x, y, SND_PIG_DIGGING);
7620       }
7621       else if (!IS_FREE(newx, newy))
7622       {
7623         if (IS_PLAYER(x, y))
7624           DrawPlayerField(x, y);
7625         else
7626           DrawLevelField(x, y);
7627
7628         return;
7629       }
7630     }
7631     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7632     {
7633       if (Store[x][y] != EL_EMPTY)
7634       {
7635         boolean can_clone = FALSE;
7636         int xx, yy;
7637
7638         /* check if element to clone is still there */
7639         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7640         {
7641           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7642           {
7643             can_clone = TRUE;
7644
7645             break;
7646           }
7647         }
7648
7649         /* cannot clone or target field not free anymore -- do not clone */
7650         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7651           Store[x][y] = EL_EMPTY;
7652       }
7653
7654       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7655       {
7656         if (IS_MV_DIAGONAL(MovDir[x][y]))
7657         {
7658           int diagonal_move_dir = MovDir[x][y];
7659           int stored = Store[x][y];
7660           int change_delay = 8;
7661           int graphic;
7662
7663           /* android is moving diagonally */
7664
7665           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7666
7667           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7668           GfxElement[x][y] = EL_EMC_ANDROID;
7669           GfxAction[x][y] = ACTION_SHRINKING;
7670           GfxDir[x][y] = diagonal_move_dir;
7671           ChangeDelay[x][y] = change_delay;
7672
7673           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7674                                    GfxDir[x][y]);
7675
7676           DrawLevelGraphicAnimation(x, y, graphic);
7677           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7678
7679           if (Feld[newx][newy] == EL_ACID)
7680           {
7681             SplashAcid(newx, newy);
7682
7683             return;
7684           }
7685
7686           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7687
7688           Store[newx][newy] = EL_EMC_ANDROID;
7689           GfxElement[newx][newy] = EL_EMC_ANDROID;
7690           GfxAction[newx][newy] = ACTION_GROWING;
7691           GfxDir[newx][newy] = diagonal_move_dir;
7692           ChangeDelay[newx][newy] = change_delay;
7693
7694           graphic = el_act_dir2img(GfxElement[newx][newy],
7695                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7696
7697           DrawLevelGraphicAnimation(newx, newy, graphic);
7698           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7699
7700           return;
7701         }
7702         else
7703         {
7704           Feld[newx][newy] = EL_EMPTY;
7705           DrawLevelField(newx, newy);
7706
7707           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7708         }
7709       }
7710       else if (!IS_FREE(newx, newy))
7711       {
7712 #if 0
7713         if (IS_PLAYER(x, y))
7714           DrawPlayerField(x, y);
7715         else
7716           DrawLevelField(x, y);
7717 #endif
7718
7719         return;
7720       }
7721     }
7722     else if (IS_CUSTOM_ELEMENT(element) &&
7723              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7724     {
7725       int new_element = Feld[newx][newy];
7726
7727       if (!IS_FREE(newx, newy))
7728       {
7729         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7730                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7731                       ACTION_BREAKING);
7732
7733         /* no element can dig solid indestructible elements */
7734         if (IS_INDESTRUCTIBLE(new_element) &&
7735             !IS_DIGGABLE(new_element) &&
7736             !IS_COLLECTIBLE(new_element))
7737           return;
7738
7739         if (AmoebaNr[newx][newy] &&
7740             (new_element == EL_AMOEBA_FULL ||
7741              new_element == EL_BD_AMOEBA ||
7742              new_element == EL_AMOEBA_GROWING))
7743         {
7744           AmoebaCnt[AmoebaNr[newx][newy]]--;
7745           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7746         }
7747
7748         if (IS_MOVING(newx, newy))
7749           RemoveMovingField(newx, newy);
7750         else
7751         {
7752           RemoveField(newx, newy);
7753           DrawLevelField(newx, newy);
7754         }
7755
7756         /* if digged element was about to explode, prevent the explosion */
7757         ExplodeField[newx][newy] = EX_TYPE_NONE;
7758
7759         PlayLevelSoundAction(x, y, action);
7760       }
7761
7762       Store[newx][newy] = EL_EMPTY;
7763 #if 1
7764       /* this makes it possible to leave the removed element again */
7765       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7766         Store[newx][newy] = new_element;
7767 #else
7768       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7769       {
7770         int move_leave_element = element_info[element].move_leave_element;
7771
7772         /* this makes it possible to leave the removed element again */
7773         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7774                              new_element : move_leave_element);
7775       }
7776 #endif
7777
7778       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7779       {
7780         RunnerVisit[x][y] = FrameCounter;
7781         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7782       }
7783     }
7784     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7785     {
7786       if (!IS_FREE(newx, newy))
7787       {
7788         if (IS_PLAYER(x, y))
7789           DrawPlayerField(x, y);
7790         else
7791           DrawLevelField(x, y);
7792
7793         return;
7794       }
7795       else
7796       {
7797         boolean wanna_flame = !RND(10);
7798         int dx = newx - x, dy = newy - y;
7799         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7800         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7801         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7802                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7803         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7804                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7805
7806         if ((wanna_flame ||
7807              IS_CLASSIC_ENEMY(element1) ||
7808              IS_CLASSIC_ENEMY(element2)) &&
7809             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7810             element1 != EL_FLAMES && element2 != EL_FLAMES)
7811         {
7812           ResetGfxAnimation(x, y);
7813           GfxAction[x][y] = ACTION_ATTACKING;
7814
7815           if (IS_PLAYER(x, y))
7816             DrawPlayerField(x, y);
7817           else
7818             DrawLevelField(x, y);
7819
7820           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7821
7822           MovDelay[x][y] = 50;
7823
7824           /* !!! */
7825 #if 0
7826           RemoveField(newx, newy);
7827 #endif
7828           Feld[newx][newy] = EL_FLAMES;
7829           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7830           {
7831 #if 0
7832             RemoveField(newx1, newy1);
7833 #endif
7834             Feld[newx1][newy1] = EL_FLAMES;
7835           }
7836           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7837           {
7838 #if 0
7839             RemoveField(newx2, newy2);
7840 #endif
7841             Feld[newx2][newy2] = EL_FLAMES;
7842           }
7843
7844           return;
7845         }
7846       }
7847     }
7848     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7849              Feld[newx][newy] == EL_DIAMOND)
7850     {
7851       if (IS_MOVING(newx, newy))
7852         RemoveMovingField(newx, newy);
7853       else
7854       {
7855         Feld[newx][newy] = EL_EMPTY;
7856         DrawLevelField(newx, newy);
7857       }
7858
7859       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7860     }
7861     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7862              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7863     {
7864       if (AmoebaNr[newx][newy])
7865       {
7866         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7867         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7868             Feld[newx][newy] == EL_BD_AMOEBA)
7869           AmoebaCnt[AmoebaNr[newx][newy]]--;
7870       }
7871
7872 #if 0
7873       /* !!! test !!! */
7874       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
7875       {
7876         RemoveMovingField(newx, newy);
7877       }
7878 #else
7879       if (IS_MOVING(newx, newy))
7880       {
7881         RemoveMovingField(newx, newy);
7882       }
7883 #endif
7884       else
7885       {
7886         Feld[newx][newy] = EL_EMPTY;
7887         DrawLevelField(newx, newy);
7888       }
7889
7890       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7891     }
7892     else if ((element == EL_PACMAN || element == EL_MOLE)
7893              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7894     {
7895       if (AmoebaNr[newx][newy])
7896       {
7897         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7898         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7899             Feld[newx][newy] == EL_BD_AMOEBA)
7900           AmoebaCnt[AmoebaNr[newx][newy]]--;
7901       }
7902
7903       if (element == EL_MOLE)
7904       {
7905         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7906         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7907
7908         ResetGfxAnimation(x, y);
7909         GfxAction[x][y] = ACTION_DIGGING;
7910         DrawLevelField(x, y);
7911
7912         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7913
7914         return;                         /* wait for shrinking amoeba */
7915       }
7916       else      /* element == EL_PACMAN */
7917       {
7918         Feld[newx][newy] = EL_EMPTY;
7919         DrawLevelField(newx, newy);
7920         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7921       }
7922     }
7923     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7924              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7925               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7926     {
7927       /* wait for shrinking amoeba to completely disappear */
7928       return;
7929     }
7930     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7931     {
7932       /* object was running against a wall */
7933
7934       TurnRound(x, y);
7935
7936 #if 0
7937       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
7938       if (move_pattern & MV_ANY_DIRECTION &&
7939           move_pattern == MovDir[x][y])
7940       {
7941         int blocking_element =
7942           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
7943
7944         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
7945                                  MovDir[x][y]);
7946
7947         element = Feld[x][y];   /* element might have changed */
7948       }
7949 #endif
7950
7951       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7952         DrawLevelElementAnimation(x, y, element);
7953
7954       if (DONT_TOUCH(element))
7955         TestIfBadThingTouchesPlayer(x, y);
7956
7957       return;
7958     }
7959
7960     InitMovingField(x, y, MovDir[x][y]);
7961
7962     PlayLevelSoundAction(x, y, ACTION_MOVING);
7963   }
7964
7965   if (MovDir[x][y])
7966     ContinueMoving(x, y);
7967 }
7968
7969 void ContinueMoving(int x, int y)
7970 {
7971   int element = Feld[x][y];
7972   struct ElementInfo *ei = &element_info[element];
7973   int direction = MovDir[x][y];
7974   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7975   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7976   int newx = x + dx, newy = y + dy;
7977   int stored = Store[x][y];
7978   int stored_new = Store[newx][newy];
7979   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7980   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7981   boolean last_line = (newy == lev_fieldy - 1);
7982
7983   MovPos[x][y] += getElementMoveStepsize(x, y);
7984
7985   if (pushed_by_player) /* special case: moving object pushed by player */
7986     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7987
7988   if (ABS(MovPos[x][y]) < TILEX)
7989   {
7990 #if 0
7991     int ee = Feld[x][y];
7992     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
7993     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
7994
7995     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
7996            x, y, ABS(MovPos[x][y]),
7997            ee, gg, ff,
7998            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
7999 #endif
8000
8001     DrawLevelField(x, y);
8002
8003     return;     /* element is still moving */
8004   }
8005
8006   /* element reached destination field */
8007
8008   Feld[x][y] = EL_EMPTY;
8009   Feld[newx][newy] = element;
8010   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8011
8012   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8013   {
8014     element = Feld[newx][newy] = EL_ACID;
8015   }
8016   else if (element == EL_MOLE)
8017   {
8018     Feld[x][y] = EL_SAND;
8019
8020     DrawLevelFieldCrumbledSandNeighbours(x, y);
8021   }
8022   else if (element == EL_QUICKSAND_FILLING)
8023   {
8024     element = Feld[newx][newy] = get_next_element(element);
8025     Store[newx][newy] = Store[x][y];
8026   }
8027   else if (element == EL_QUICKSAND_EMPTYING)
8028   {
8029     Feld[x][y] = get_next_element(element);
8030     element = Feld[newx][newy] = Store[x][y];
8031   }
8032   else if (element == EL_QUICKSAND_FAST_FILLING)
8033   {
8034     element = Feld[newx][newy] = get_next_element(element);
8035     Store[newx][newy] = Store[x][y];
8036   }
8037   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8038   {
8039     Feld[x][y] = get_next_element(element);
8040     element = Feld[newx][newy] = Store[x][y];
8041   }
8042   else if (element == EL_MAGIC_WALL_FILLING)
8043   {
8044     element = Feld[newx][newy] = get_next_element(element);
8045     if (!game.magic_wall_active)
8046       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8047     Store[newx][newy] = Store[x][y];
8048   }
8049   else if (element == EL_MAGIC_WALL_EMPTYING)
8050   {
8051     Feld[x][y] = get_next_element(element);
8052     if (!game.magic_wall_active)
8053       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8054     element = Feld[newx][newy] = Store[x][y];
8055
8056 #if USE_NEW_CUSTOM_VALUE
8057     InitField(newx, newy, FALSE);
8058 #endif
8059   }
8060   else if (element == EL_BD_MAGIC_WALL_FILLING)
8061   {
8062     element = Feld[newx][newy] = get_next_element(element);
8063     if (!game.magic_wall_active)
8064       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8065     Store[newx][newy] = Store[x][y];
8066   }
8067   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8068   {
8069     Feld[x][y] = get_next_element(element);
8070     if (!game.magic_wall_active)
8071       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8072     element = Feld[newx][newy] = Store[x][y];
8073
8074 #if USE_NEW_CUSTOM_VALUE
8075     InitField(newx, newy, FALSE);
8076 #endif
8077   }
8078   else if (element == EL_DC_MAGIC_WALL_FILLING)
8079   {
8080     element = Feld[newx][newy] = get_next_element(element);
8081     if (!game.magic_wall_active)
8082       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8083     Store[newx][newy] = Store[x][y];
8084   }
8085   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8086   {
8087     Feld[x][y] = get_next_element(element);
8088     if (!game.magic_wall_active)
8089       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8090     element = Feld[newx][newy] = Store[x][y];
8091
8092 #if USE_NEW_CUSTOM_VALUE
8093     InitField(newx, newy, FALSE);
8094 #endif
8095   }
8096   else if (element == EL_AMOEBA_DROPPING)
8097   {
8098     Feld[x][y] = get_next_element(element);
8099     element = Feld[newx][newy] = Store[x][y];
8100   }
8101   else if (element == EL_SOKOBAN_OBJECT)
8102   {
8103     if (Back[x][y])
8104       Feld[x][y] = Back[x][y];
8105
8106     if (Back[newx][newy])
8107       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8108
8109     Back[x][y] = Back[newx][newy] = 0;
8110   }
8111
8112   Store[x][y] = EL_EMPTY;
8113   MovPos[x][y] = 0;
8114   MovDir[x][y] = 0;
8115   MovDelay[x][y] = 0;
8116
8117   MovDelay[newx][newy] = 0;
8118
8119   if (CAN_CHANGE_OR_HAS_ACTION(element))
8120   {
8121     /* copy element change control values to new field */
8122     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8123     ChangePage[newx][newy]  = ChangePage[x][y];
8124     ChangeCount[newx][newy] = ChangeCount[x][y];
8125     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8126   }
8127
8128 #if USE_NEW_CUSTOM_VALUE
8129     CustomValue[newx][newy] = CustomValue[x][y];
8130 #endif
8131
8132   ChangeDelay[x][y] = 0;
8133   ChangePage[x][y] = -1;
8134   ChangeCount[x][y] = 0;
8135   ChangeEvent[x][y] = -1;
8136
8137 #if USE_NEW_CUSTOM_VALUE
8138   CustomValue[x][y] = 0;
8139 #endif
8140
8141   /* copy animation control values to new field */
8142   GfxFrame[newx][newy]  = GfxFrame[x][y];
8143   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8144   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8145   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8146
8147   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8148
8149   /* some elements can leave other elements behind after moving */
8150 #if 1
8151   if (ei->move_leave_element != EL_EMPTY &&
8152       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8153       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8154 #else
8155   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8156       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8157       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8158 #endif
8159   {
8160     int move_leave_element = ei->move_leave_element;
8161
8162 #if 1
8163 #if 1
8164     /* this makes it possible to leave the removed element again */
8165     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8166       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8167 #else
8168     /* this makes it possible to leave the removed element again */
8169     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8170       move_leave_element = stored;
8171 #endif
8172 #else
8173     /* this makes it possible to leave the removed element again */
8174     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8175         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8176       move_leave_element = stored;
8177 #endif
8178
8179     Feld[x][y] = move_leave_element;
8180
8181     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8182       MovDir[x][y] = direction;
8183
8184     InitField(x, y, FALSE);
8185
8186     if (GFX_CRUMBLED(Feld[x][y]))
8187       DrawLevelFieldCrumbledSandNeighbours(x, y);
8188
8189     if (ELEM_IS_PLAYER(move_leave_element))
8190       RelocatePlayer(x, y, move_leave_element);
8191   }
8192
8193   /* do this after checking for left-behind element */
8194   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8195
8196   if (!CAN_MOVE(element) ||
8197       (CAN_FALL(element) && direction == MV_DOWN &&
8198        (element == EL_SPRING ||
8199         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8200         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8201     GfxDir[x][y] = MovDir[newx][newy] = 0;
8202
8203   DrawLevelField(x, y);
8204   DrawLevelField(newx, newy);
8205
8206   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8207
8208   /* prevent pushed element from moving on in pushed direction */
8209   if (pushed_by_player && CAN_MOVE(element) &&
8210       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8211       !(element_info[element].move_pattern & direction))
8212     TurnRound(newx, newy);
8213
8214   /* prevent elements on conveyor belt from moving on in last direction */
8215   if (pushed_by_conveyor && CAN_FALL(element) &&
8216       direction & MV_HORIZONTAL)
8217     MovDir[newx][newy] = 0;
8218
8219   if (!pushed_by_player)
8220   {
8221     int nextx = newx + dx, nexty = newy + dy;
8222     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8223
8224     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8225
8226     if (CAN_FALL(element) && direction == MV_DOWN)
8227       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8228
8229     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8230       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8231
8232 #if USE_FIX_IMPACT_COLLISION
8233     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8234       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8235 #endif
8236   }
8237
8238   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8239   {
8240     TestIfBadThingTouchesPlayer(newx, newy);
8241     TestIfBadThingTouchesFriend(newx, newy);
8242
8243     if (!IS_CUSTOM_ELEMENT(element))
8244       TestIfBadThingTouchesOtherBadThing(newx, newy);
8245   }
8246   else if (element == EL_PENGUIN)
8247     TestIfFriendTouchesBadThing(newx, newy);
8248
8249   /* give the player one last chance (one more frame) to move away */
8250   if (CAN_FALL(element) && direction == MV_DOWN &&
8251       (last_line || (!IS_FREE(x, newy + 1) &&
8252                      (!IS_PLAYER(x, newy + 1) ||
8253                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8254     Impact(x, newy);
8255
8256   if (pushed_by_player && !game.use_change_when_pushing_bug)
8257   {
8258     int push_side = MV_DIR_OPPOSITE(direction);
8259     struct PlayerInfo *player = PLAYERINFO(x, y);
8260
8261     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8262                                player->index_bit, push_side);
8263     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8264                                         player->index_bit, push_side);
8265   }
8266
8267   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8268     MovDelay[newx][newy] = 1;
8269
8270   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8271
8272   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8273
8274 #if 0
8275   if (ChangePage[newx][newy] != -1)             /* delayed change */
8276   {
8277     int page = ChangePage[newx][newy];
8278     struct ElementChangeInfo *change = &ei->change_page[page];
8279
8280     ChangePage[newx][newy] = -1;
8281
8282     if (change->can_change)
8283     {
8284       if (ChangeElement(newx, newy, element, page))
8285       {
8286         if (change->post_change_function)
8287           change->post_change_function(newx, newy);
8288       }
8289     }
8290
8291     if (change->has_action)
8292       ExecuteCustomElementAction(newx, newy, element, page);
8293   }
8294 #endif
8295
8296   TestIfElementHitsCustomElement(newx, newy, direction);
8297   TestIfPlayerTouchesCustomElement(newx, newy);
8298   TestIfElementTouchesCustomElement(newx, newy);
8299
8300   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8301       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8302     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8303                              MV_DIR_OPPOSITE(direction));
8304 }
8305
8306 int AmoebeNachbarNr(int ax, int ay)
8307 {
8308   int i;
8309   int element = Feld[ax][ay];
8310   int group_nr = 0;
8311   static int xy[4][2] =
8312   {
8313     { 0, -1 },
8314     { -1, 0 },
8315     { +1, 0 },
8316     { 0, +1 }
8317   };
8318
8319   for (i = 0; i < NUM_DIRECTIONS; i++)
8320   {
8321     int x = ax + xy[i][0];
8322     int y = ay + xy[i][1];
8323
8324     if (!IN_LEV_FIELD(x, y))
8325       continue;
8326
8327     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8328       group_nr = AmoebaNr[x][y];
8329   }
8330
8331   return group_nr;
8332 }
8333
8334 void AmoebenVereinigen(int ax, int ay)
8335 {
8336   int i, x, y, xx, yy;
8337   int new_group_nr = AmoebaNr[ax][ay];
8338   static int xy[4][2] =
8339   {
8340     { 0, -1 },
8341     { -1, 0 },
8342     { +1, 0 },
8343     { 0, +1 }
8344   };
8345
8346   if (new_group_nr == 0)
8347     return;
8348
8349   for (i = 0; i < NUM_DIRECTIONS; i++)
8350   {
8351     x = ax + xy[i][0];
8352     y = ay + xy[i][1];
8353
8354     if (!IN_LEV_FIELD(x, y))
8355       continue;
8356
8357     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8358          Feld[x][y] == EL_BD_AMOEBA ||
8359          Feld[x][y] == EL_AMOEBA_DEAD) &&
8360         AmoebaNr[x][y] != new_group_nr)
8361     {
8362       int old_group_nr = AmoebaNr[x][y];
8363
8364       if (old_group_nr == 0)
8365         return;
8366
8367       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8368       AmoebaCnt[old_group_nr] = 0;
8369       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8370       AmoebaCnt2[old_group_nr] = 0;
8371
8372       SCAN_PLAYFIELD(xx, yy)
8373       {
8374         if (AmoebaNr[xx][yy] == old_group_nr)
8375           AmoebaNr[xx][yy] = new_group_nr;
8376       }
8377     }
8378   }
8379 }
8380
8381 void AmoebeUmwandeln(int ax, int ay)
8382 {
8383   int i, x, y;
8384
8385   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8386   {
8387     int group_nr = AmoebaNr[ax][ay];
8388
8389 #ifdef DEBUG
8390     if (group_nr == 0)
8391     {
8392       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8393       printf("AmoebeUmwandeln(): This should never happen!\n");
8394       return;
8395     }
8396 #endif
8397
8398     SCAN_PLAYFIELD(x, y)
8399     {
8400       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8401       {
8402         AmoebaNr[x][y] = 0;
8403         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8404       }
8405     }
8406
8407     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8408                             SND_AMOEBA_TURNING_TO_GEM :
8409                             SND_AMOEBA_TURNING_TO_ROCK));
8410     Bang(ax, ay);
8411   }
8412   else
8413   {
8414     static int xy[4][2] =
8415     {
8416       { 0, -1 },
8417       { -1, 0 },
8418       { +1, 0 },
8419       { 0, +1 }
8420     };
8421
8422     for (i = 0; i < NUM_DIRECTIONS; i++)
8423     {
8424       x = ax + xy[i][0];
8425       y = ay + xy[i][1];
8426
8427       if (!IN_LEV_FIELD(x, y))
8428         continue;
8429
8430       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8431       {
8432         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8433                               SND_AMOEBA_TURNING_TO_GEM :
8434                               SND_AMOEBA_TURNING_TO_ROCK));
8435         Bang(x, y);
8436       }
8437     }
8438   }
8439 }
8440
8441 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8442 {
8443   int x, y;
8444   int group_nr = AmoebaNr[ax][ay];
8445   boolean done = FALSE;
8446
8447 #ifdef DEBUG
8448   if (group_nr == 0)
8449   {
8450     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8451     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8452     return;
8453   }
8454 #endif
8455
8456   SCAN_PLAYFIELD(x, y)
8457   {
8458     if (AmoebaNr[x][y] == group_nr &&
8459         (Feld[x][y] == EL_AMOEBA_DEAD ||
8460          Feld[x][y] == EL_BD_AMOEBA ||
8461          Feld[x][y] == EL_AMOEBA_GROWING))
8462     {
8463       AmoebaNr[x][y] = 0;
8464       Feld[x][y] = new_element;
8465       InitField(x, y, FALSE);
8466       DrawLevelField(x, y);
8467       done = TRUE;
8468     }
8469   }
8470
8471   if (done)
8472     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8473                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8474                             SND_BD_AMOEBA_TURNING_TO_GEM));
8475 }
8476
8477 void AmoebeWaechst(int x, int y)
8478 {
8479   static unsigned long sound_delay = 0;
8480   static unsigned long sound_delay_value = 0;
8481
8482   if (!MovDelay[x][y])          /* start new growing cycle */
8483   {
8484     MovDelay[x][y] = 7;
8485
8486     if (DelayReached(&sound_delay, sound_delay_value))
8487     {
8488       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8489       sound_delay_value = 30;
8490     }
8491   }
8492
8493   if (MovDelay[x][y])           /* wait some time before growing bigger */
8494   {
8495     MovDelay[x][y]--;
8496     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8497     {
8498       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8499                                            6 - MovDelay[x][y]);
8500
8501       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8502     }
8503
8504     if (!MovDelay[x][y])
8505     {
8506       Feld[x][y] = Store[x][y];
8507       Store[x][y] = 0;
8508       DrawLevelField(x, y);
8509     }
8510   }
8511 }
8512
8513 void AmoebaDisappearing(int x, int y)
8514 {
8515   static unsigned long sound_delay = 0;
8516   static unsigned long sound_delay_value = 0;
8517
8518   if (!MovDelay[x][y])          /* start new shrinking cycle */
8519   {
8520     MovDelay[x][y] = 7;
8521
8522     if (DelayReached(&sound_delay, sound_delay_value))
8523       sound_delay_value = 30;
8524   }
8525
8526   if (MovDelay[x][y])           /* wait some time before shrinking */
8527   {
8528     MovDelay[x][y]--;
8529     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8530     {
8531       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8532                                            6 - MovDelay[x][y]);
8533
8534       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8535     }
8536
8537     if (!MovDelay[x][y])
8538     {
8539       Feld[x][y] = EL_EMPTY;
8540       DrawLevelField(x, y);
8541
8542       /* don't let mole enter this field in this cycle;
8543          (give priority to objects falling to this field from above) */
8544       Stop[x][y] = TRUE;
8545     }
8546   }
8547 }
8548
8549 void AmoebeAbleger(int ax, int ay)
8550 {
8551   int i;
8552   int element = Feld[ax][ay];
8553   int graphic = el2img(element);
8554   int newax = ax, neway = ay;
8555   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8556   static int xy[4][2] =
8557   {
8558     { 0, -1 },
8559     { -1, 0 },
8560     { +1, 0 },
8561     { 0, +1 }
8562   };
8563
8564   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8565   {
8566     Feld[ax][ay] = EL_AMOEBA_DEAD;
8567     DrawLevelField(ax, ay);
8568     return;
8569   }
8570
8571   if (IS_ANIMATED(graphic))
8572     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8573
8574   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8575     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8576
8577   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8578   {
8579     MovDelay[ax][ay]--;
8580     if (MovDelay[ax][ay])
8581       return;
8582   }
8583
8584   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8585   {
8586     int start = RND(4);
8587     int x = ax + xy[start][0];
8588     int y = ay + xy[start][1];
8589
8590     if (!IN_LEV_FIELD(x, y))
8591       return;
8592
8593     if (IS_FREE(x, y) ||
8594         CAN_GROW_INTO(Feld[x][y]) ||
8595         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8596         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8597     {
8598       newax = x;
8599       neway = y;
8600     }
8601
8602     if (newax == ax && neway == ay)
8603       return;
8604   }
8605   else                          /* normal or "filled" (BD style) amoeba */
8606   {
8607     int start = RND(4);
8608     boolean waiting_for_player = FALSE;
8609
8610     for (i = 0; i < NUM_DIRECTIONS; i++)
8611     {
8612       int j = (start + i) % 4;
8613       int x = ax + xy[j][0];
8614       int y = ay + xy[j][1];
8615
8616       if (!IN_LEV_FIELD(x, y))
8617         continue;
8618
8619       if (IS_FREE(x, y) ||
8620           CAN_GROW_INTO(Feld[x][y]) ||
8621           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8622           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8623       {
8624         newax = x;
8625         neway = y;
8626         break;
8627       }
8628       else if (IS_PLAYER(x, y))
8629         waiting_for_player = TRUE;
8630     }
8631
8632     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8633     {
8634       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8635       {
8636         Feld[ax][ay] = EL_AMOEBA_DEAD;
8637         DrawLevelField(ax, ay);
8638         AmoebaCnt[AmoebaNr[ax][ay]]--;
8639
8640         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8641         {
8642           if (element == EL_AMOEBA_FULL)
8643             AmoebeUmwandeln(ax, ay);
8644           else if (element == EL_BD_AMOEBA)
8645             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8646         }
8647       }
8648       return;
8649     }
8650     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8651     {
8652       /* amoeba gets larger by growing in some direction */
8653
8654       int new_group_nr = AmoebaNr[ax][ay];
8655
8656 #ifdef DEBUG
8657   if (new_group_nr == 0)
8658   {
8659     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8660     printf("AmoebeAbleger(): This should never happen!\n");
8661     return;
8662   }
8663 #endif
8664
8665       AmoebaNr[newax][neway] = new_group_nr;
8666       AmoebaCnt[new_group_nr]++;
8667       AmoebaCnt2[new_group_nr]++;
8668
8669       /* if amoeba touches other amoeba(s) after growing, unify them */
8670       AmoebenVereinigen(newax, neway);
8671
8672       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8673       {
8674         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8675         return;
8676       }
8677     }
8678   }
8679
8680   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8681       (neway == lev_fieldy - 1 && newax != ax))
8682   {
8683     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8684     Store[newax][neway] = element;
8685   }
8686   else if (neway == ay || element == EL_EMC_DRIPPER)
8687   {
8688     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8689
8690     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8691   }
8692   else
8693   {
8694     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8695     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8696     Store[ax][ay] = EL_AMOEBA_DROP;
8697     ContinueMoving(ax, ay);
8698     return;
8699   }
8700
8701   DrawLevelField(newax, neway);
8702 }
8703
8704 void Life(int ax, int ay)
8705 {
8706   int x1, y1, x2, y2;
8707   int life_time = 40;
8708   int element = Feld[ax][ay];
8709   int graphic = el2img(element);
8710   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8711                          level.biomaze);
8712   boolean changed = FALSE;
8713
8714   if (IS_ANIMATED(graphic))
8715     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8716
8717   if (Stop[ax][ay])
8718     return;
8719
8720   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8721     MovDelay[ax][ay] = life_time;
8722
8723   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8724   {
8725     MovDelay[ax][ay]--;
8726     if (MovDelay[ax][ay])
8727       return;
8728   }
8729
8730   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8731   {
8732     int xx = ax+x1, yy = ay+y1;
8733     int nachbarn = 0;
8734
8735     if (!IN_LEV_FIELD(xx, yy))
8736       continue;
8737
8738     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8739     {
8740       int x = xx+x2, y = yy+y2;
8741
8742       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8743         continue;
8744
8745       if (((Feld[x][y] == element ||
8746             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8747            !Stop[x][y]) ||
8748           (IS_FREE(x, y) && Stop[x][y]))
8749         nachbarn++;
8750     }
8751
8752     if (xx == ax && yy == ay)           /* field in the middle */
8753     {
8754       if (nachbarn < life_parameter[0] ||
8755           nachbarn > life_parameter[1])
8756       {
8757         Feld[xx][yy] = EL_EMPTY;
8758         if (!Stop[xx][yy])
8759           DrawLevelField(xx, yy);
8760         Stop[xx][yy] = TRUE;
8761         changed = TRUE;
8762       }
8763     }
8764     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8765     {                                   /* free border field */
8766       if (nachbarn >= life_parameter[2] &&
8767           nachbarn <= life_parameter[3])
8768       {
8769         Feld[xx][yy] = element;
8770         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8771         if (!Stop[xx][yy])
8772           DrawLevelField(xx, yy);
8773         Stop[xx][yy] = TRUE;
8774         changed = TRUE;
8775       }
8776     }
8777   }
8778
8779   if (changed)
8780     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8781                    SND_GAME_OF_LIFE_GROWING);
8782 }
8783
8784 static void InitRobotWheel(int x, int y)
8785 {
8786   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8787 }
8788
8789 static void RunRobotWheel(int x, int y)
8790 {
8791   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8792 }
8793
8794 static void StopRobotWheel(int x, int y)
8795 {
8796   if (ZX == x && ZY == y)
8797   {
8798     ZX = ZY = -1;
8799
8800     game.robot_wheel_active = FALSE;
8801   }
8802 }
8803
8804 static void InitTimegateWheel(int x, int y)
8805 {
8806   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8807 }
8808
8809 static void RunTimegateWheel(int x, int y)
8810 {
8811   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8812 }
8813
8814 static void InitMagicBallDelay(int x, int y)
8815 {
8816 #if 1
8817   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8818 #else
8819   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8820 #endif
8821 }
8822
8823 static void ActivateMagicBall(int bx, int by)
8824 {
8825   int x, y;
8826
8827   if (level.ball_random)
8828   {
8829     int pos_border = RND(8);    /* select one of the eight border elements */
8830     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8831     int xx = pos_content % 3;
8832     int yy = pos_content / 3;
8833
8834     x = bx - 1 + xx;
8835     y = by - 1 + yy;
8836
8837     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8838       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8839   }
8840   else
8841   {
8842     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8843     {
8844       int xx = x - bx + 1;
8845       int yy = y - by + 1;
8846
8847       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8848         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8849     }
8850   }
8851
8852   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8853 }
8854
8855 void CheckExit(int x, int y)
8856 {
8857   if (local_player->gems_still_needed > 0 ||
8858       local_player->sokobanfields_still_needed > 0 ||
8859       local_player->lights_still_needed > 0)
8860   {
8861     int element = Feld[x][y];
8862     int graphic = el2img(element);
8863
8864     if (IS_ANIMATED(graphic))
8865       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8866
8867     return;
8868   }
8869
8870   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8871     return;
8872
8873   Feld[x][y] = EL_EXIT_OPENING;
8874
8875   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8876 }
8877
8878 void CheckExitEM(int x, int y)
8879 {
8880   if (local_player->gems_still_needed > 0 ||
8881       local_player->sokobanfields_still_needed > 0 ||
8882       local_player->lights_still_needed > 0)
8883   {
8884     int element = Feld[x][y];
8885     int graphic = el2img(element);
8886
8887     if (IS_ANIMATED(graphic))
8888       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8889
8890     return;
8891   }
8892
8893   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8894     return;
8895
8896   Feld[x][y] = EL_EM_EXIT_OPENING;
8897
8898   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8899 }
8900
8901 void CheckExitSteel(int x, int y)
8902 {
8903   if (local_player->gems_still_needed > 0 ||
8904       local_player->sokobanfields_still_needed > 0 ||
8905       local_player->lights_still_needed > 0)
8906   {
8907     int element = Feld[x][y];
8908     int graphic = el2img(element);
8909
8910     if (IS_ANIMATED(graphic))
8911       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8912
8913     return;
8914   }
8915
8916   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8917     return;
8918
8919   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8920
8921   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8922 }
8923
8924 void CheckExitSteelEM(int x, int y)
8925 {
8926   if (local_player->gems_still_needed > 0 ||
8927       local_player->sokobanfields_still_needed > 0 ||
8928       local_player->lights_still_needed > 0)
8929   {
8930     int element = Feld[x][y];
8931     int graphic = el2img(element);
8932
8933     if (IS_ANIMATED(graphic))
8934       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8935
8936     return;
8937   }
8938
8939   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8940     return;
8941
8942   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8943
8944   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8945 }
8946
8947 void CheckExitSP(int x, int y)
8948 {
8949   if (local_player->gems_still_needed > 0)
8950   {
8951     int element = Feld[x][y];
8952     int graphic = el2img(element);
8953
8954     if (IS_ANIMATED(graphic))
8955       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8956
8957     return;
8958   }
8959
8960   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8961     return;
8962
8963   Feld[x][y] = EL_SP_EXIT_OPENING;
8964
8965   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8966 }
8967
8968 static void CloseAllOpenTimegates()
8969 {
8970   int x, y;
8971
8972   SCAN_PLAYFIELD(x, y)
8973   {
8974     int element = Feld[x][y];
8975
8976     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8977     {
8978       Feld[x][y] = EL_TIMEGATE_CLOSING;
8979
8980       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8981     }
8982   }
8983 }
8984
8985 void DrawTwinkleOnField(int x, int y)
8986 {
8987   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8988     return;
8989
8990   if (Feld[x][y] == EL_BD_DIAMOND)
8991     return;
8992
8993   if (MovDelay[x][y] == 0)      /* next animation frame */
8994     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8995
8996   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8997   {
8998     MovDelay[x][y]--;
8999
9000     if (setup.direct_draw && MovDelay[x][y])
9001       SetDrawtoField(DRAW_BUFFERED);
9002
9003     DrawLevelElementAnimation(x, y, Feld[x][y]);
9004
9005     if (MovDelay[x][y] != 0)
9006     {
9007       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9008                                            10 - MovDelay[x][y]);
9009
9010       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9011
9012       if (setup.direct_draw)
9013       {
9014         int dest_x, dest_y;
9015
9016         dest_x = FX + SCREENX(x) * TILEX;
9017         dest_y = FY + SCREENY(y) * TILEY;
9018
9019         BlitBitmap(drawto_field, window,
9020                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9021         SetDrawtoField(DRAW_DIRECT);
9022       }
9023     }
9024   }
9025 }
9026
9027 void MauerWaechst(int x, int y)
9028 {
9029   int delay = 6;
9030
9031   if (!MovDelay[x][y])          /* next animation frame */
9032     MovDelay[x][y] = 3 * delay;
9033
9034   if (MovDelay[x][y])           /* wait some time before next frame */
9035   {
9036     MovDelay[x][y]--;
9037
9038     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9039     {
9040       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9041       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9042
9043       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9044     }
9045
9046     if (!MovDelay[x][y])
9047     {
9048       if (MovDir[x][y] == MV_LEFT)
9049       {
9050         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9051           DrawLevelField(x - 1, y);
9052       }
9053       else if (MovDir[x][y] == MV_RIGHT)
9054       {
9055         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9056           DrawLevelField(x + 1, y);
9057       }
9058       else if (MovDir[x][y] == MV_UP)
9059       {
9060         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9061           DrawLevelField(x, y - 1);
9062       }
9063       else
9064       {
9065         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9066           DrawLevelField(x, y + 1);
9067       }
9068
9069       Feld[x][y] = Store[x][y];
9070       Store[x][y] = 0;
9071       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9072       DrawLevelField(x, y);
9073     }
9074   }
9075 }
9076
9077 void MauerAbleger(int ax, int ay)
9078 {
9079   int element = Feld[ax][ay];
9080   int graphic = el2img(element);
9081   boolean oben_frei = FALSE, unten_frei = FALSE;
9082   boolean links_frei = FALSE, rechts_frei = FALSE;
9083   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9084   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9085   boolean new_wall = FALSE;
9086
9087   if (IS_ANIMATED(graphic))
9088     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9089
9090   if (!MovDelay[ax][ay])        /* start building new wall */
9091     MovDelay[ax][ay] = 6;
9092
9093   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9094   {
9095     MovDelay[ax][ay]--;
9096     if (MovDelay[ax][ay])
9097       return;
9098   }
9099
9100   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9101     oben_frei = TRUE;
9102   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9103     unten_frei = TRUE;
9104   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9105     links_frei = TRUE;
9106   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9107     rechts_frei = TRUE;
9108
9109   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9110       element == EL_EXPANDABLE_WALL_ANY)
9111   {
9112     if (oben_frei)
9113     {
9114       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9115       Store[ax][ay-1] = element;
9116       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9117       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9118         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9119                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9120       new_wall = TRUE;
9121     }
9122     if (unten_frei)
9123     {
9124       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9125       Store[ax][ay+1] = element;
9126       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9127       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9128         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9129                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9130       new_wall = TRUE;
9131     }
9132   }
9133
9134   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9135       element == EL_EXPANDABLE_WALL_ANY ||
9136       element == EL_EXPANDABLE_WALL ||
9137       element == EL_BD_EXPANDABLE_WALL)
9138   {
9139     if (links_frei)
9140     {
9141       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9142       Store[ax-1][ay] = element;
9143       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9144       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9145         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9146                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9147       new_wall = TRUE;
9148     }
9149
9150     if (rechts_frei)
9151     {
9152       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9153       Store[ax+1][ay] = element;
9154       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9155       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9156         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9157                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9158       new_wall = TRUE;
9159     }
9160   }
9161
9162   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9163     DrawLevelField(ax, ay);
9164
9165   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9166     oben_massiv = TRUE;
9167   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9168     unten_massiv = TRUE;
9169   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9170     links_massiv = TRUE;
9171   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9172     rechts_massiv = TRUE;
9173
9174   if (((oben_massiv && unten_massiv) ||
9175        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9176        element == EL_EXPANDABLE_WALL) &&
9177       ((links_massiv && rechts_massiv) ||
9178        element == EL_EXPANDABLE_WALL_VERTICAL))
9179     Feld[ax][ay] = EL_WALL;
9180
9181   if (new_wall)
9182     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9183 }
9184
9185 void MauerAblegerStahl(int ax, int ay)
9186 {
9187   int element = Feld[ax][ay];
9188   int graphic = el2img(element);
9189   boolean oben_frei = FALSE, unten_frei = FALSE;
9190   boolean links_frei = FALSE, rechts_frei = FALSE;
9191   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9192   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9193   boolean new_wall = FALSE;
9194
9195   if (IS_ANIMATED(graphic))
9196     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9197
9198   if (!MovDelay[ax][ay])        /* start building new wall */
9199     MovDelay[ax][ay] = 6;
9200
9201   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9202   {
9203     MovDelay[ax][ay]--;
9204     if (MovDelay[ax][ay])
9205       return;
9206   }
9207
9208   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9209     oben_frei = TRUE;
9210   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9211     unten_frei = TRUE;
9212   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9213     links_frei = TRUE;
9214   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9215     rechts_frei = TRUE;
9216
9217   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9218       element == EL_EXPANDABLE_STEELWALL_ANY)
9219   {
9220     if (oben_frei)
9221     {
9222       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9223       Store[ax][ay-1] = element;
9224       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9225       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9226         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9227                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9228       new_wall = TRUE;
9229     }
9230     if (unten_frei)
9231     {
9232       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9233       Store[ax][ay+1] = element;
9234       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9235       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9236         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9237                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9238       new_wall = TRUE;
9239     }
9240   }
9241
9242   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9243       element == EL_EXPANDABLE_STEELWALL_ANY)
9244   {
9245     if (links_frei)
9246     {
9247       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9248       Store[ax-1][ay] = element;
9249       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9250       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9251         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9252                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9253       new_wall = TRUE;
9254     }
9255
9256     if (rechts_frei)
9257     {
9258       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9259       Store[ax+1][ay] = element;
9260       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9261       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9262         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9263                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9264       new_wall = TRUE;
9265     }
9266   }
9267
9268   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9269     oben_massiv = TRUE;
9270   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9271     unten_massiv = TRUE;
9272   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9273     links_massiv = TRUE;
9274   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9275     rechts_massiv = TRUE;
9276
9277   if (((oben_massiv && unten_massiv) ||
9278        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9279       ((links_massiv && rechts_massiv) ||
9280        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9281     Feld[ax][ay] = EL_WALL;
9282
9283   if (new_wall)
9284     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9285 }
9286
9287 void CheckForDragon(int x, int y)
9288 {
9289   int i, j;
9290   boolean dragon_found = FALSE;
9291   static int xy[4][2] =
9292   {
9293     { 0, -1 },
9294     { -1, 0 },
9295     { +1, 0 },
9296     { 0, +1 }
9297   };
9298
9299   for (i = 0; i < NUM_DIRECTIONS; i++)
9300   {
9301     for (j = 0; j < 4; j++)
9302     {
9303       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9304
9305       if (IN_LEV_FIELD(xx, yy) &&
9306           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9307       {
9308         if (Feld[xx][yy] == EL_DRAGON)
9309           dragon_found = TRUE;
9310       }
9311       else
9312         break;
9313     }
9314   }
9315
9316   if (!dragon_found)
9317   {
9318     for (i = 0; i < NUM_DIRECTIONS; i++)
9319     {
9320       for (j = 0; j < 3; j++)
9321       {
9322         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9323   
9324         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9325         {
9326           Feld[xx][yy] = EL_EMPTY;
9327           DrawLevelField(xx, yy);
9328         }
9329         else
9330           break;
9331       }
9332     }
9333   }
9334 }
9335
9336 static void InitBuggyBase(int x, int y)
9337 {
9338   int element = Feld[x][y];
9339   int activating_delay = FRAMES_PER_SECOND / 4;
9340
9341   ChangeDelay[x][y] =
9342     (element == EL_SP_BUGGY_BASE ?
9343      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9344      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9345      activating_delay :
9346      element == EL_SP_BUGGY_BASE_ACTIVE ?
9347      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9348 }
9349
9350 static void WarnBuggyBase(int x, int y)
9351 {
9352   int i;
9353   static int xy[4][2] =
9354   {
9355     { 0, -1 },
9356     { -1, 0 },
9357     { +1, 0 },
9358     { 0, +1 }
9359   };
9360
9361   for (i = 0; i < NUM_DIRECTIONS; i++)
9362   {
9363     int xx = x + xy[i][0];
9364     int yy = y + xy[i][1];
9365
9366     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9367     {
9368       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9369
9370       break;
9371     }
9372   }
9373 }
9374
9375 static void InitTrap(int x, int y)
9376 {
9377   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9378 }
9379
9380 static void ActivateTrap(int x, int y)
9381 {
9382   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9383 }
9384
9385 static void ChangeActiveTrap(int x, int y)
9386 {
9387   int graphic = IMG_TRAP_ACTIVE;
9388
9389   /* if new animation frame was drawn, correct crumbled sand border */
9390   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9391     DrawLevelFieldCrumbledSand(x, y);
9392 }
9393
9394 static int getSpecialActionElement(int element, int number, int base_element)
9395 {
9396   return (element != EL_EMPTY ? element :
9397           number != -1 ? base_element + number - 1 :
9398           EL_EMPTY);
9399 }
9400
9401 static int getModifiedActionNumber(int value_old, int operator, int operand,
9402                                    int value_min, int value_max)
9403 {
9404   int value_new = (operator == CA_MODE_SET      ? operand :
9405                    operator == CA_MODE_ADD      ? value_old + operand :
9406                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9407                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9408                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9409                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9410                    value_old);
9411
9412   return (value_new < value_min ? value_min :
9413           value_new > value_max ? value_max :
9414           value_new);
9415 }
9416
9417 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9418 {
9419   struct ElementInfo *ei = &element_info[element];
9420   struct ElementChangeInfo *change = &ei->change_page[page];
9421   int target_element = change->target_element;
9422   int action_type = change->action_type;
9423   int action_mode = change->action_mode;
9424   int action_arg = change->action_arg;
9425   int i;
9426
9427   if (!change->has_action)
9428     return;
9429
9430   /* ---------- determine action paramater values -------------------------- */
9431
9432   int level_time_value =
9433     (level.time > 0 ? TimeLeft :
9434      TimePlayed);
9435
9436   int action_arg_element =
9437     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9438      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9439      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9440      EL_EMPTY);
9441
9442   int action_arg_direction =
9443     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9444      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9445      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9446      change->actual_trigger_side :
9447      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9448      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9449      MV_NONE);
9450
9451   int action_arg_number_min =
9452     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9453      CA_ARG_MIN);
9454
9455   int action_arg_number_max =
9456     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9457      action_type == CA_SET_LEVEL_GEMS ? 999 :
9458      action_type == CA_SET_LEVEL_TIME ? 9999 :
9459      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9460      action_type == CA_SET_CE_VALUE ? 9999 :
9461      action_type == CA_SET_CE_SCORE ? 9999 :
9462      CA_ARG_MAX);
9463
9464   int action_arg_number_reset =
9465     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9466      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9467      action_type == CA_SET_LEVEL_TIME ? level.time :
9468      action_type == CA_SET_LEVEL_SCORE ? 0 :
9469 #if USE_NEW_CUSTOM_VALUE
9470      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9471 #else
9472      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9473 #endif
9474      action_type == CA_SET_CE_SCORE ? 0 :
9475      0);
9476
9477   int action_arg_number =
9478     (action_arg <= CA_ARG_MAX ? action_arg :
9479      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9480      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9481      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9482      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9483      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9484      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9485 #if USE_NEW_CUSTOM_VALUE
9486      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9487 #else
9488      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9489 #endif
9490      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9491      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9492      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9493      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9494      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9495      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9496      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9497      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9498      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9499      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9500      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9501      -1);
9502
9503   int action_arg_number_old =
9504     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9505      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9506      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9507      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9508      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9509      0);
9510
9511   int action_arg_number_new =
9512     getModifiedActionNumber(action_arg_number_old,
9513                             action_mode, action_arg_number,
9514                             action_arg_number_min, action_arg_number_max);
9515
9516   int trigger_player_bits =
9517     (change->actual_trigger_player >= EL_PLAYER_1 &&
9518      change->actual_trigger_player <= EL_PLAYER_4 ?
9519      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9520      PLAYER_BITS_ANY);
9521
9522   int action_arg_player_bits =
9523     (action_arg >= CA_ARG_PLAYER_1 &&
9524      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9525      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9526      PLAYER_BITS_ANY);
9527
9528   /* ---------- execute action  -------------------------------------------- */
9529
9530   switch (action_type)
9531   {
9532     case CA_NO_ACTION:
9533     {
9534       return;
9535     }
9536
9537     /* ---------- level actions  ------------------------------------------- */
9538
9539     case CA_RESTART_LEVEL:
9540     {
9541       game.restart_level = TRUE;
9542
9543       break;
9544     }
9545
9546     case CA_SHOW_ENVELOPE:
9547     {
9548       int element = getSpecialActionElement(action_arg_element,
9549                                             action_arg_number, EL_ENVELOPE_1);
9550
9551       if (IS_ENVELOPE(element))
9552         local_player->show_envelope = element;
9553
9554       break;
9555     }
9556
9557     case CA_SET_LEVEL_TIME:
9558     {
9559       if (level.time > 0)       /* only modify limited time value */
9560       {
9561         TimeLeft = action_arg_number_new;
9562
9563 #if 1
9564         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9565
9566         DisplayGameControlValues();
9567 #else
9568         DrawGameValue_Time(TimeLeft);
9569 #endif
9570
9571         if (!TimeLeft && setup.time_limit)
9572           for (i = 0; i < MAX_PLAYERS; i++)
9573             KillPlayer(&stored_player[i]);
9574       }
9575
9576       break;
9577     }
9578
9579     case CA_SET_LEVEL_SCORE:
9580     {
9581       local_player->score = action_arg_number_new;
9582
9583 #if 1
9584       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9585
9586       DisplayGameControlValues();
9587 #else
9588       DrawGameValue_Score(local_player->score);
9589 #endif
9590
9591       break;
9592     }
9593
9594     case CA_SET_LEVEL_GEMS:
9595     {
9596       local_player->gems_still_needed = action_arg_number_new;
9597
9598 #if 1
9599       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9600
9601       DisplayGameControlValues();
9602 #else
9603       DrawGameValue_Emeralds(local_player->gems_still_needed);
9604 #endif
9605
9606       break;
9607     }
9608
9609 #if !USE_PLAYER_GRAVITY
9610     case CA_SET_LEVEL_GRAVITY:
9611     {
9612       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9613                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9614                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9615                       game.gravity);
9616       break;
9617     }
9618 #endif
9619
9620     case CA_SET_LEVEL_WIND:
9621     {
9622       game.wind_direction = action_arg_direction;
9623
9624       break;
9625     }
9626
9627     /* ---------- player actions  ------------------------------------------ */
9628
9629     case CA_MOVE_PLAYER:
9630     {
9631       /* automatically move to the next field in specified direction */
9632       for (i = 0; i < MAX_PLAYERS; i++)
9633         if (trigger_player_bits & (1 << i))
9634           stored_player[i].programmed_action = action_arg_direction;
9635
9636       break;
9637     }
9638
9639     case CA_EXIT_PLAYER:
9640     {
9641       for (i = 0; i < MAX_PLAYERS; i++)
9642         if (action_arg_player_bits & (1 << i))
9643           PlayerWins(&stored_player[i]);
9644
9645       break;
9646     }
9647
9648     case CA_KILL_PLAYER:
9649     {
9650       for (i = 0; i < MAX_PLAYERS; i++)
9651         if (action_arg_player_bits & (1 << i))
9652           KillPlayer(&stored_player[i]);
9653
9654       break;
9655     }
9656
9657     case CA_SET_PLAYER_KEYS:
9658     {
9659       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9660       int element = getSpecialActionElement(action_arg_element,
9661                                             action_arg_number, EL_KEY_1);
9662
9663       if (IS_KEY(element))
9664       {
9665         for (i = 0; i < MAX_PLAYERS; i++)
9666         {
9667           if (trigger_player_bits & (1 << i))
9668           {
9669             stored_player[i].key[KEY_NR(element)] = key_state;
9670
9671             DrawGameDoorValues();
9672           }
9673         }
9674       }
9675
9676       break;
9677     }
9678
9679     case CA_SET_PLAYER_SPEED:
9680     {
9681       for (i = 0; i < MAX_PLAYERS; i++)
9682       {
9683         if (trigger_player_bits & (1 << i))
9684         {
9685           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9686
9687           if (action_arg == CA_ARG_SPEED_FASTER &&
9688               stored_player[i].cannot_move)
9689           {
9690             action_arg_number = STEPSIZE_VERY_SLOW;
9691           }
9692           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9693                    action_arg == CA_ARG_SPEED_FASTER)
9694           {
9695             action_arg_number = 2;
9696             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9697                            CA_MODE_MULTIPLY);
9698           }
9699           else if (action_arg == CA_ARG_NUMBER_RESET)
9700           {
9701             action_arg_number = level.initial_player_stepsize[i];
9702           }
9703
9704           move_stepsize =
9705             getModifiedActionNumber(move_stepsize,
9706                                     action_mode,
9707                                     action_arg_number,
9708                                     action_arg_number_min,
9709                                     action_arg_number_max);
9710
9711           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9712         }
9713       }
9714
9715       break;
9716     }
9717
9718     case CA_SET_PLAYER_SHIELD:
9719     {
9720       for (i = 0; i < MAX_PLAYERS; i++)
9721       {
9722         if (trigger_player_bits & (1 << i))
9723         {
9724           if (action_arg == CA_ARG_SHIELD_OFF)
9725           {
9726             stored_player[i].shield_normal_time_left = 0;
9727             stored_player[i].shield_deadly_time_left = 0;
9728           }
9729           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9730           {
9731             stored_player[i].shield_normal_time_left = 999999;
9732           }
9733           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9734           {
9735             stored_player[i].shield_normal_time_left = 999999;
9736             stored_player[i].shield_deadly_time_left = 999999;
9737           }
9738         }
9739       }
9740
9741       break;
9742     }
9743
9744 #if USE_PLAYER_GRAVITY
9745     case CA_SET_PLAYER_GRAVITY:
9746     {
9747       for (i = 0; i < MAX_PLAYERS; i++)
9748       {
9749         if (trigger_player_bits & (1 << i))
9750         {
9751           stored_player[i].gravity =
9752             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9753              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9754              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9755              stored_player[i].gravity);
9756         }
9757       }
9758
9759       break;
9760     }
9761 #endif
9762
9763     case CA_SET_PLAYER_ARTWORK:
9764     {
9765       for (i = 0; i < MAX_PLAYERS; i++)
9766       {
9767         if (trigger_player_bits & (1 << i))
9768         {
9769           int artwork_element = action_arg_element;
9770
9771           if (action_arg == CA_ARG_ELEMENT_RESET)
9772             artwork_element =
9773               (level.use_artwork_element[i] ? level.artwork_element[i] :
9774                stored_player[i].element_nr);
9775
9776 #if USE_GFX_RESET_PLAYER_ARTWORK
9777           if (stored_player[i].artwork_element != artwork_element)
9778             stored_player[i].Frame = 0;
9779 #endif
9780
9781           stored_player[i].artwork_element = artwork_element;
9782
9783           SetPlayerWaiting(&stored_player[i], FALSE);
9784
9785           /* set number of special actions for bored and sleeping animation */
9786           stored_player[i].num_special_action_bored =
9787             get_num_special_action(artwork_element,
9788                                    ACTION_BORING_1, ACTION_BORING_LAST);
9789           stored_player[i].num_special_action_sleeping =
9790             get_num_special_action(artwork_element,
9791                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9792         }
9793       }
9794
9795       break;
9796     }
9797
9798     /* ---------- CE actions  ---------------------------------------------- */
9799
9800     case CA_SET_CE_VALUE:
9801     {
9802 #if USE_NEW_CUSTOM_VALUE
9803       int last_ce_value = CustomValue[x][y];
9804
9805       CustomValue[x][y] = action_arg_number_new;
9806
9807       if (CustomValue[x][y] != last_ce_value)
9808       {
9809         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9810         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9811
9812         if (CustomValue[x][y] == 0)
9813         {
9814           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9815           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9816         }
9817       }
9818 #endif
9819
9820       break;
9821     }
9822
9823     case CA_SET_CE_SCORE:
9824     {
9825 #if USE_NEW_CUSTOM_VALUE
9826       int last_ce_score = ei->collect_score;
9827
9828       ei->collect_score = action_arg_number_new;
9829
9830       if (ei->collect_score != last_ce_score)
9831       {
9832         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9833         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9834
9835         if (ei->collect_score == 0)
9836         {
9837           int xx, yy;
9838
9839           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9840           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9841
9842           /*
9843             This is a very special case that seems to be a mixture between
9844             CheckElementChange() and CheckTriggeredElementChange(): while
9845             the first one only affects single elements that are triggered
9846             directly, the second one affects multiple elements in the playfield
9847             that are triggered indirectly by another element. This is a third
9848             case: Changing the CE score always affects multiple identical CEs,
9849             so every affected CE must be checked, not only the single CE for
9850             which the CE score was changed in the first place (as every instance
9851             of that CE shares the same CE score, and therefore also can change)!
9852           */
9853           SCAN_PLAYFIELD(xx, yy)
9854           {
9855             if (Feld[xx][yy] == element)
9856               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9857                                  CE_SCORE_GETS_ZERO);
9858           }
9859         }
9860       }
9861 #endif
9862
9863       break;
9864     }
9865
9866     /* ---------- engine actions  ------------------------------------------ */
9867
9868     case CA_SET_ENGINE_SCAN_MODE:
9869     {
9870       InitPlayfieldScanMode(action_arg);
9871
9872       break;
9873     }
9874
9875     default:
9876       break;
9877   }
9878 }
9879
9880 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9881 {
9882   int old_element = Feld[x][y];
9883   int new_element = GetElementFromGroupElement(element);
9884   int previous_move_direction = MovDir[x][y];
9885 #if USE_NEW_CUSTOM_VALUE
9886   int last_ce_value = CustomValue[x][y];
9887 #endif
9888   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9889   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9890   boolean add_player_onto_element = (new_element_is_player &&
9891 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
9892                                      /* this breaks SnakeBite when a snake is
9893                                         halfway through a door that closes */
9894                                      /* NOW FIXED AT LEVEL INIT IN files.c */
9895                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9896 #endif
9897                                      IS_WALKABLE(old_element));
9898
9899 #if 0
9900   /* check if element under the player changes from accessible to unaccessible
9901      (needed for special case of dropping element which then changes) */
9902   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9903       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9904   {
9905     Bang(x, y);
9906
9907     return;
9908   }
9909 #endif
9910
9911   if (!add_player_onto_element)
9912   {
9913     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9914       RemoveMovingField(x, y);
9915     else
9916       RemoveField(x, y);
9917
9918     Feld[x][y] = new_element;
9919
9920 #if !USE_GFX_RESET_GFX_ANIMATION
9921     ResetGfxAnimation(x, y);
9922     ResetRandomAnimationValue(x, y);
9923 #endif
9924
9925     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9926       MovDir[x][y] = previous_move_direction;
9927
9928 #if USE_NEW_CUSTOM_VALUE
9929     if (element_info[new_element].use_last_ce_value)
9930       CustomValue[x][y] = last_ce_value;
9931 #endif
9932
9933     InitField_WithBug1(x, y, FALSE);
9934
9935     new_element = Feld[x][y];   /* element may have changed */
9936
9937 #if USE_GFX_RESET_GFX_ANIMATION
9938     ResetGfxAnimation(x, y);
9939     ResetRandomAnimationValue(x, y);
9940 #endif
9941
9942     DrawLevelField(x, y);
9943
9944     if (GFX_CRUMBLED(new_element))
9945       DrawLevelFieldCrumbledSandNeighbours(x, y);
9946   }
9947
9948 #if 1
9949   /* check if element under the player changes from accessible to unaccessible
9950      (needed for special case of dropping element which then changes) */
9951   /* (must be checked after creating new element for walkable group elements) */
9952 #if USE_FIX_KILLED_BY_NON_WALKABLE
9953   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9954       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9955   {
9956     Bang(x, y);
9957
9958     return;
9959   }
9960 #else
9961   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
9962       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9963   {
9964     Bang(x, y);
9965
9966     return;
9967   }
9968 #endif
9969 #endif
9970
9971   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9972   if (new_element_is_player)
9973     RelocatePlayer(x, y, new_element);
9974
9975   if (is_change)
9976     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9977
9978   TestIfBadThingTouchesPlayer(x, y);
9979   TestIfPlayerTouchesCustomElement(x, y);
9980   TestIfElementTouchesCustomElement(x, y);
9981 }
9982
9983 static void CreateField(int x, int y, int element)
9984 {
9985   CreateFieldExt(x, y, element, FALSE);
9986 }
9987
9988 static void CreateElementFromChange(int x, int y, int element)
9989 {
9990   element = GET_VALID_RUNTIME_ELEMENT(element);
9991
9992 #if USE_STOP_CHANGED_ELEMENTS
9993   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9994   {
9995     int old_element = Feld[x][y];
9996
9997     /* prevent changed element from moving in same engine frame
9998        unless both old and new element can either fall or move */
9999     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10000         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10001       Stop[x][y] = TRUE;
10002   }
10003 #endif
10004
10005   CreateFieldExt(x, y, element, TRUE);
10006 }
10007
10008 static boolean ChangeElement(int x, int y, int element, int page)
10009 {
10010   struct ElementInfo *ei = &element_info[element];
10011   struct ElementChangeInfo *change = &ei->change_page[page];
10012   int ce_value = CustomValue[x][y];
10013   int ce_score = ei->collect_score;
10014   int target_element;
10015   int old_element = Feld[x][y];
10016
10017   /* always use default change event to prevent running into a loop */
10018   if (ChangeEvent[x][y] == -1)
10019     ChangeEvent[x][y] = CE_DELAY;
10020
10021   if (ChangeEvent[x][y] == CE_DELAY)
10022   {
10023     /* reset actual trigger element, trigger player and action element */
10024     change->actual_trigger_element = EL_EMPTY;
10025     change->actual_trigger_player = EL_PLAYER_1;
10026     change->actual_trigger_side = CH_SIDE_NONE;
10027     change->actual_trigger_ce_value = 0;
10028     change->actual_trigger_ce_score = 0;
10029   }
10030
10031   /* do not change elements more than a specified maximum number of changes */
10032   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10033     return FALSE;
10034
10035   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10036
10037   if (change->explode)
10038   {
10039     Bang(x, y);
10040
10041     return TRUE;
10042   }
10043
10044   if (change->use_target_content)
10045   {
10046     boolean complete_replace = TRUE;
10047     boolean can_replace[3][3];
10048     int xx, yy;
10049
10050     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10051     {
10052       boolean is_empty;
10053       boolean is_walkable;
10054       boolean is_diggable;
10055       boolean is_collectible;
10056       boolean is_removable;
10057       boolean is_destructible;
10058       int ex = x + xx - 1;
10059       int ey = y + yy - 1;
10060       int content_element = change->target_content.e[xx][yy];
10061       int e;
10062
10063       can_replace[xx][yy] = TRUE;
10064
10065       if (ex == x && ey == y)   /* do not check changing element itself */
10066         continue;
10067
10068       if (content_element == EL_EMPTY_SPACE)
10069       {
10070         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10071
10072         continue;
10073       }
10074
10075       if (!IN_LEV_FIELD(ex, ey))
10076       {
10077         can_replace[xx][yy] = FALSE;
10078         complete_replace = FALSE;
10079
10080         continue;
10081       }
10082
10083       e = Feld[ex][ey];
10084
10085       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10086         e = MovingOrBlocked2Element(ex, ey);
10087
10088       is_empty = (IS_FREE(ex, ey) ||
10089                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10090
10091       is_walkable     = (is_empty || IS_WALKABLE(e));
10092       is_diggable     = (is_empty || IS_DIGGABLE(e));
10093       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10094       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10095       is_removable    = (is_diggable || is_collectible);
10096
10097       can_replace[xx][yy] =
10098         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10099           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10100           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10101           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10102           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10103           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10104          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10105
10106       if (!can_replace[xx][yy])
10107         complete_replace = FALSE;
10108     }
10109
10110     if (!change->only_if_complete || complete_replace)
10111     {
10112       boolean something_has_changed = FALSE;
10113
10114       if (change->only_if_complete && change->use_random_replace &&
10115           RND(100) < change->random_percentage)
10116         return FALSE;
10117
10118       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10119       {
10120         int ex = x + xx - 1;
10121         int ey = y + yy - 1;
10122         int content_element;
10123
10124         if (can_replace[xx][yy] && (!change->use_random_replace ||
10125                                     RND(100) < change->random_percentage))
10126         {
10127           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10128             RemoveMovingField(ex, ey);
10129
10130           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10131
10132           content_element = change->target_content.e[xx][yy];
10133           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10134                                               ce_value, ce_score);
10135
10136           CreateElementFromChange(ex, ey, target_element);
10137
10138           something_has_changed = TRUE;
10139
10140           /* for symmetry reasons, freeze newly created border elements */
10141           if (ex != x || ey != y)
10142             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10143         }
10144       }
10145
10146       if (something_has_changed)
10147       {
10148         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10149         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10150       }
10151     }
10152   }
10153   else
10154   {
10155     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10156                                         ce_value, ce_score);
10157
10158     if (element == EL_DIAGONAL_GROWING ||
10159         element == EL_DIAGONAL_SHRINKING)
10160     {
10161       target_element = Store[x][y];
10162
10163       Store[x][y] = EL_EMPTY;
10164     }
10165
10166     CreateElementFromChange(x, y, target_element);
10167
10168     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10169     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10170   }
10171
10172   /* this uses direct change before indirect change */
10173   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10174
10175   return TRUE;
10176 }
10177
10178 #if USE_NEW_DELAYED_ACTION
10179
10180 static void HandleElementChange(int x, int y, int page)
10181 {
10182   int element = MovingOrBlocked2Element(x, y);
10183   struct ElementInfo *ei = &element_info[element];
10184   struct ElementChangeInfo *change = &ei->change_page[page];
10185
10186 #ifdef DEBUG
10187   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10188       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10189   {
10190     printf("\n\n");
10191     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10192            x, y, element, element_info[element].token_name);
10193     printf("HandleElementChange(): This should never happen!\n");
10194     printf("\n\n");
10195   }
10196 #endif
10197
10198   /* this can happen with classic bombs on walkable, changing elements */
10199   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10200   {
10201 #if 0
10202     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10203       ChangeDelay[x][y] = 0;
10204 #endif
10205
10206     return;
10207   }
10208
10209   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10210   {
10211     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10212
10213     if (change->can_change)
10214     {
10215 #if 1
10216       /* !!! not clear why graphic animation should be reset at all here !!! */
10217       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10218 #if USE_GFX_RESET_WHEN_NOT_MOVING
10219       /* when a custom element is about to change (for example by change delay),
10220          do not reset graphic animation when the custom element is moving */
10221       if (!IS_MOVING(x, y))
10222 #endif
10223       {
10224         ResetGfxAnimation(x, y);
10225         ResetRandomAnimationValue(x, y);
10226       }
10227 #endif
10228
10229       if (change->pre_change_function)
10230         change->pre_change_function(x, y);
10231     }
10232   }
10233
10234   ChangeDelay[x][y]--;
10235
10236   if (ChangeDelay[x][y] != 0)           /* continue element change */
10237   {
10238     if (change->can_change)
10239     {
10240       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10241
10242       if (IS_ANIMATED(graphic))
10243         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10244
10245       if (change->change_function)
10246         change->change_function(x, y);
10247     }
10248   }
10249   else                                  /* finish element change */
10250   {
10251     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10252     {
10253       page = ChangePage[x][y];
10254       ChangePage[x][y] = -1;
10255
10256       change = &ei->change_page[page];
10257     }
10258
10259     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10260     {
10261       ChangeDelay[x][y] = 1;            /* try change after next move step */
10262       ChangePage[x][y] = page;          /* remember page to use for change */
10263
10264       return;
10265     }
10266
10267     if (change->can_change)
10268     {
10269       if (ChangeElement(x, y, element, page))
10270       {
10271         if (change->post_change_function)
10272           change->post_change_function(x, y);
10273       }
10274     }
10275
10276     if (change->has_action)
10277       ExecuteCustomElementAction(x, y, element, page);
10278   }
10279 }
10280
10281 #else
10282
10283 static void HandleElementChange(int x, int y, int page)
10284 {
10285   int element = MovingOrBlocked2Element(x, y);
10286   struct ElementInfo *ei = &element_info[element];
10287   struct ElementChangeInfo *change = &ei->change_page[page];
10288
10289 #ifdef DEBUG
10290   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10291   {
10292     printf("\n\n");
10293     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10294            x, y, element, element_info[element].token_name);
10295     printf("HandleElementChange(): This should never happen!\n");
10296     printf("\n\n");
10297   }
10298 #endif
10299
10300   /* this can happen with classic bombs on walkable, changing elements */
10301   if (!CAN_CHANGE(element))
10302   {
10303 #if 0
10304     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10305       ChangeDelay[x][y] = 0;
10306 #endif
10307
10308     return;
10309   }
10310
10311   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10312   {
10313     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10314
10315     ResetGfxAnimation(x, y);
10316     ResetRandomAnimationValue(x, y);
10317
10318     if (change->pre_change_function)
10319       change->pre_change_function(x, y);
10320   }
10321
10322   ChangeDelay[x][y]--;
10323
10324   if (ChangeDelay[x][y] != 0)           /* continue element change */
10325   {
10326     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10327
10328     if (IS_ANIMATED(graphic))
10329       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10330
10331     if (change->change_function)
10332       change->change_function(x, y);
10333   }
10334   else                                  /* finish element change */
10335   {
10336     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10337     {
10338       page = ChangePage[x][y];
10339       ChangePage[x][y] = -1;
10340
10341       change = &ei->change_page[page];
10342     }
10343
10344     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10345     {
10346       ChangeDelay[x][y] = 1;            /* try change after next move step */
10347       ChangePage[x][y] = page;          /* remember page to use for change */
10348
10349       return;
10350     }
10351
10352     if (ChangeElement(x, y, element, page))
10353     {
10354       if (change->post_change_function)
10355         change->post_change_function(x, y);
10356     }
10357   }
10358 }
10359
10360 #endif
10361
10362 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10363                                               int trigger_element,
10364                                               int trigger_event,
10365                                               int trigger_player,
10366                                               int trigger_side,
10367                                               int trigger_page)
10368 {
10369   boolean change_done_any = FALSE;
10370   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10371   int i;
10372
10373   if (!(trigger_events[trigger_element][trigger_event]))
10374     return FALSE;
10375
10376 #if 0
10377   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10378          trigger_event, recursion_loop_depth, recursion_loop_detected,
10379          recursion_loop_element, EL_NAME(recursion_loop_element));
10380 #endif
10381
10382   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10383
10384   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10385   {
10386     int element = EL_CUSTOM_START + i;
10387     boolean change_done = FALSE;
10388     int p;
10389
10390     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10391         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10392       continue;
10393
10394     for (p = 0; p < element_info[element].num_change_pages; p++)
10395     {
10396       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10397
10398       if (change->can_change_or_has_action &&
10399           change->has_event[trigger_event] &&
10400           change->trigger_side & trigger_side &&
10401           change->trigger_player & trigger_player &&
10402           change->trigger_page & trigger_page_bits &&
10403           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10404       {
10405         change->actual_trigger_element = trigger_element;
10406         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10407         change->actual_trigger_side = trigger_side;
10408         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10409         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10410
10411         if ((change->can_change && !change_done) || change->has_action)
10412         {
10413           int x, y;
10414
10415           SCAN_PLAYFIELD(x, y)
10416           {
10417             if (Feld[x][y] == element)
10418             {
10419               if (change->can_change && !change_done)
10420               {
10421                 ChangeDelay[x][y] = 1;
10422                 ChangeEvent[x][y] = trigger_event;
10423
10424                 HandleElementChange(x, y, p);
10425               }
10426 #if USE_NEW_DELAYED_ACTION
10427               else if (change->has_action)
10428               {
10429                 ExecuteCustomElementAction(x, y, element, p);
10430                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10431               }
10432 #else
10433               if (change->has_action)
10434               {
10435                 ExecuteCustomElementAction(x, y, element, p);
10436                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10437               }
10438 #endif
10439             }
10440           }
10441
10442           if (change->can_change)
10443           {
10444             change_done = TRUE;
10445             change_done_any = TRUE;
10446           }
10447         }
10448       }
10449     }
10450   }
10451
10452   RECURSION_LOOP_DETECTION_END();
10453
10454   return change_done_any;
10455 }
10456
10457 static boolean CheckElementChangeExt(int x, int y,
10458                                      int element,
10459                                      int trigger_element,
10460                                      int trigger_event,
10461                                      int trigger_player,
10462                                      int trigger_side)
10463 {
10464   boolean change_done = FALSE;
10465   int p;
10466
10467   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10468       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10469     return FALSE;
10470
10471   if (Feld[x][y] == EL_BLOCKED)
10472   {
10473     Blocked2Moving(x, y, &x, &y);
10474     element = Feld[x][y];
10475   }
10476
10477 #if 0
10478   /* check if element has already changed */
10479   if (Feld[x][y] != element)
10480     return FALSE;
10481 #else
10482   /* check if element has already changed or is about to change after moving */
10483   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10484        Feld[x][y] != element) ||
10485
10486       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10487        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10488         ChangePage[x][y] != -1)))
10489     return FALSE;
10490 #endif
10491
10492 #if 0
10493   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10494          trigger_event, recursion_loop_depth, recursion_loop_detected,
10495          recursion_loop_element, EL_NAME(recursion_loop_element));
10496 #endif
10497
10498   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10499
10500   for (p = 0; p < element_info[element].num_change_pages; p++)
10501   {
10502     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10503
10504     /* check trigger element for all events where the element that is checked
10505        for changing interacts with a directly adjacent element -- this is
10506        different to element changes that affect other elements to change on the
10507        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10508     boolean check_trigger_element =
10509       (trigger_event == CE_TOUCHING_X ||
10510        trigger_event == CE_HITTING_X ||
10511        trigger_event == CE_HIT_BY_X ||
10512 #if 1
10513        /* this one was forgotten until 3.2.3 */
10514        trigger_event == CE_DIGGING_X);
10515 #endif
10516
10517     if (change->can_change_or_has_action &&
10518         change->has_event[trigger_event] &&
10519         change->trigger_side & trigger_side &&
10520         change->trigger_player & trigger_player &&
10521         (!check_trigger_element ||
10522          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10523     {
10524       change->actual_trigger_element = trigger_element;
10525       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10526       change->actual_trigger_side = trigger_side;
10527       change->actual_trigger_ce_value = CustomValue[x][y];
10528       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10529
10530       /* special case: trigger element not at (x,y) position for some events */
10531       if (check_trigger_element)
10532       {
10533         static struct
10534         {
10535           int dx, dy;
10536         } move_xy[] =
10537           {
10538             {  0,  0 },
10539             { -1,  0 },
10540             { +1,  0 },
10541             {  0,  0 },
10542             {  0, -1 },
10543             {  0,  0 }, { 0, 0 }, { 0, 0 },
10544             {  0, +1 }
10545           };
10546
10547         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10548         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10549
10550         change->actual_trigger_ce_value = CustomValue[xx][yy];
10551         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10552       }
10553
10554       if (change->can_change && !change_done)
10555       {
10556         ChangeDelay[x][y] = 1;
10557         ChangeEvent[x][y] = trigger_event;
10558
10559         HandleElementChange(x, y, p);
10560
10561         change_done = TRUE;
10562       }
10563 #if USE_NEW_DELAYED_ACTION
10564       else if (change->has_action)
10565       {
10566         ExecuteCustomElementAction(x, y, element, p);
10567         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10568       }
10569 #else
10570       if (change->has_action)
10571       {
10572         ExecuteCustomElementAction(x, y, element, p);
10573         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10574       }
10575 #endif
10576     }
10577   }
10578
10579   RECURSION_LOOP_DETECTION_END();
10580
10581   return change_done;
10582 }
10583
10584 static void PlayPlayerSound(struct PlayerInfo *player)
10585 {
10586   int jx = player->jx, jy = player->jy;
10587   int sound_element = player->artwork_element;
10588   int last_action = player->last_action_waiting;
10589   int action = player->action_waiting;
10590
10591   if (player->is_waiting)
10592   {
10593     if (action != last_action)
10594       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10595     else
10596       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10597   }
10598   else
10599   {
10600     if (action != last_action)
10601       StopSound(element_info[sound_element].sound[last_action]);
10602
10603     if (last_action == ACTION_SLEEPING)
10604       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10605   }
10606 }
10607
10608 static void PlayAllPlayersSound()
10609 {
10610   int i;
10611
10612   for (i = 0; i < MAX_PLAYERS; i++)
10613     if (stored_player[i].active)
10614       PlayPlayerSound(&stored_player[i]);
10615 }
10616
10617 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10618 {
10619   boolean last_waiting = player->is_waiting;
10620   int move_dir = player->MovDir;
10621
10622   player->dir_waiting = move_dir;
10623   player->last_action_waiting = player->action_waiting;
10624
10625   if (is_waiting)
10626   {
10627     if (!last_waiting)          /* not waiting -> waiting */
10628     {
10629       player->is_waiting = TRUE;
10630
10631       player->frame_counter_bored =
10632         FrameCounter +
10633         game.player_boring_delay_fixed +
10634         GetSimpleRandom(game.player_boring_delay_random);
10635       player->frame_counter_sleeping =
10636         FrameCounter +
10637         game.player_sleeping_delay_fixed +
10638         GetSimpleRandom(game.player_sleeping_delay_random);
10639
10640       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10641     }
10642
10643     if (game.player_sleeping_delay_fixed +
10644         game.player_sleeping_delay_random > 0 &&
10645         player->anim_delay_counter == 0 &&
10646         player->post_delay_counter == 0 &&
10647         FrameCounter >= player->frame_counter_sleeping)
10648       player->is_sleeping = TRUE;
10649     else if (game.player_boring_delay_fixed +
10650              game.player_boring_delay_random > 0 &&
10651              FrameCounter >= player->frame_counter_bored)
10652       player->is_bored = TRUE;
10653
10654     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10655                               player->is_bored ? ACTION_BORING :
10656                               ACTION_WAITING);
10657
10658     if (player->is_sleeping && player->use_murphy)
10659     {
10660       /* special case for sleeping Murphy when leaning against non-free tile */
10661
10662       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10663           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10664            !IS_MOVING(player->jx - 1, player->jy)))
10665         move_dir = MV_LEFT;
10666       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10667                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10668                 !IS_MOVING(player->jx + 1, player->jy)))
10669         move_dir = MV_RIGHT;
10670       else
10671         player->is_sleeping = FALSE;
10672
10673       player->dir_waiting = move_dir;
10674     }
10675
10676     if (player->is_sleeping)
10677     {
10678       if (player->num_special_action_sleeping > 0)
10679       {
10680         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10681         {
10682           int last_special_action = player->special_action_sleeping;
10683           int num_special_action = player->num_special_action_sleeping;
10684           int special_action =
10685             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10686              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10687              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10688              last_special_action + 1 : ACTION_SLEEPING);
10689           int special_graphic =
10690             el_act_dir2img(player->artwork_element, special_action, move_dir);
10691
10692           player->anim_delay_counter =
10693             graphic_info[special_graphic].anim_delay_fixed +
10694             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10695           player->post_delay_counter =
10696             graphic_info[special_graphic].post_delay_fixed +
10697             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10698
10699           player->special_action_sleeping = special_action;
10700         }
10701
10702         if (player->anim_delay_counter > 0)
10703         {
10704           player->action_waiting = player->special_action_sleeping;
10705           player->anim_delay_counter--;
10706         }
10707         else if (player->post_delay_counter > 0)
10708         {
10709           player->post_delay_counter--;
10710         }
10711       }
10712     }
10713     else if (player->is_bored)
10714     {
10715       if (player->num_special_action_bored > 0)
10716       {
10717         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10718         {
10719           int special_action =
10720             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10721           int special_graphic =
10722             el_act_dir2img(player->artwork_element, special_action, move_dir);
10723
10724           player->anim_delay_counter =
10725             graphic_info[special_graphic].anim_delay_fixed +
10726             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10727           player->post_delay_counter =
10728             graphic_info[special_graphic].post_delay_fixed +
10729             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10730
10731           player->special_action_bored = special_action;
10732         }
10733
10734         if (player->anim_delay_counter > 0)
10735         {
10736           player->action_waiting = player->special_action_bored;
10737           player->anim_delay_counter--;
10738         }
10739         else if (player->post_delay_counter > 0)
10740         {
10741           player->post_delay_counter--;
10742         }
10743       }
10744     }
10745   }
10746   else if (last_waiting)        /* waiting -> not waiting */
10747   {
10748     player->is_waiting = FALSE;
10749     player->is_bored = FALSE;
10750     player->is_sleeping = FALSE;
10751
10752     player->frame_counter_bored = -1;
10753     player->frame_counter_sleeping = -1;
10754
10755     player->anim_delay_counter = 0;
10756     player->post_delay_counter = 0;
10757
10758     player->dir_waiting = player->MovDir;
10759     player->action_waiting = ACTION_DEFAULT;
10760
10761     player->special_action_bored = ACTION_DEFAULT;
10762     player->special_action_sleeping = ACTION_DEFAULT;
10763   }
10764 }
10765
10766 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10767 {
10768   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10769   int left      = player_action & JOY_LEFT;
10770   int right     = player_action & JOY_RIGHT;
10771   int up        = player_action & JOY_UP;
10772   int down      = player_action & JOY_DOWN;
10773   int button1   = player_action & JOY_BUTTON_1;
10774   int button2   = player_action & JOY_BUTTON_2;
10775   int dx        = (left ? -1 : right ? 1 : 0);
10776   int dy        = (up   ? -1 : down  ? 1 : 0);
10777
10778   if (!player->active || tape.pausing)
10779     return 0;
10780
10781   if (player_action)
10782   {
10783     if (button1)
10784       snapped = SnapField(player, dx, dy);
10785     else
10786     {
10787       if (button2)
10788         dropped = DropElement(player);
10789
10790       moved = MovePlayer(player, dx, dy);
10791     }
10792
10793     if (tape.single_step && tape.recording && !tape.pausing)
10794     {
10795       if (button1 || (dropped && !moved))
10796       {
10797         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10798         SnapField(player, 0, 0);                /* stop snapping */
10799       }
10800     }
10801
10802     SetPlayerWaiting(player, FALSE);
10803
10804     return player_action;
10805   }
10806   else
10807   {
10808     /* no actions for this player (no input at player's configured device) */
10809
10810     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10811     SnapField(player, 0, 0);
10812     CheckGravityMovementWhenNotMoving(player);
10813
10814     if (player->MovPos == 0)
10815       SetPlayerWaiting(player, TRUE);
10816
10817     if (player->MovPos == 0)    /* needed for tape.playing */
10818       player->is_moving = FALSE;
10819
10820     player->is_dropping = FALSE;
10821     player->is_dropping_pressed = FALSE;
10822     player->drop_pressed_delay = 0;
10823
10824     return 0;
10825   }
10826 }
10827
10828 static void CheckLevelTime()
10829 {
10830   int i;
10831
10832   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10833   {
10834     if (level.native_em_level->lev->home == 0)  /* all players at home */
10835     {
10836       PlayerWins(local_player);
10837
10838       AllPlayersGone = TRUE;
10839
10840       level.native_em_level->lev->home = -1;
10841     }
10842
10843     if (level.native_em_level->ply[0]->alive == 0 &&
10844         level.native_em_level->ply[1]->alive == 0 &&
10845         level.native_em_level->ply[2]->alive == 0 &&
10846         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10847       AllPlayersGone = TRUE;
10848   }
10849
10850   if (TimeFrames >= FRAMES_PER_SECOND)
10851   {
10852     TimeFrames = 0;
10853     TapeTime++;
10854
10855     for (i = 0; i < MAX_PLAYERS; i++)
10856     {
10857       struct PlayerInfo *player = &stored_player[i];
10858
10859       if (SHIELD_ON(player))
10860       {
10861         player->shield_normal_time_left--;
10862
10863         if (player->shield_deadly_time_left > 0)
10864           player->shield_deadly_time_left--;
10865       }
10866     }
10867
10868     if (!local_player->LevelSolved && !level.use_step_counter)
10869     {
10870       TimePlayed++;
10871
10872       if (TimeLeft > 0)
10873       {
10874         TimeLeft--;
10875
10876         if (TimeLeft <= 10 && setup.time_limit)
10877           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10878
10879 #if 1
10880         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10881
10882         DisplayGameControlValues();
10883 #else
10884         DrawGameValue_Time(TimeLeft);
10885 #endif
10886
10887         if (!TimeLeft && setup.time_limit)
10888         {
10889           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10890             level.native_em_level->lev->killed_out_of_time = TRUE;
10891           else
10892             for (i = 0; i < MAX_PLAYERS; i++)
10893               KillPlayer(&stored_player[i]);
10894         }
10895       }
10896 #if 1
10897       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10898       {
10899         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10900
10901         DisplayGameControlValues();
10902       }
10903 #else
10904       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
10905         DrawGameValue_Time(TimePlayed);
10906 #endif
10907
10908       level.native_em_level->lev->time =
10909         (level.time == 0 ? TimePlayed : TimeLeft);
10910     }
10911
10912     if (tape.recording || tape.playing)
10913       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10914   }
10915
10916   UpdateGameDoorValues();
10917   DrawGameDoorValues();
10918 }
10919
10920 void AdvanceFrameAndPlayerCounters(int player_nr)
10921 {
10922   int i;
10923
10924   /* advance frame counters (global frame counter and time frame counter) */
10925   FrameCounter++;
10926   TimeFrames++;
10927
10928   /* advance player counters (counters for move delay, move animation etc.) */
10929   for (i = 0; i < MAX_PLAYERS; i++)
10930   {
10931     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10932     int move_delay_value = stored_player[i].move_delay_value;
10933     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10934
10935     if (!advance_player_counters)       /* not all players may be affected */
10936       continue;
10937
10938 #if USE_NEW_PLAYER_ANIM
10939     if (move_frames == 0)       /* less than one move per game frame */
10940     {
10941       int stepsize = TILEX / move_delay_value;
10942       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10943       int count = (stored_player[i].is_moving ?
10944                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10945
10946       if (count % delay == 0)
10947         move_frames = 1;
10948     }
10949 #endif
10950
10951     stored_player[i].Frame += move_frames;
10952
10953     if (stored_player[i].MovPos != 0)
10954       stored_player[i].StepFrame += move_frames;
10955
10956     if (stored_player[i].move_delay > 0)
10957       stored_player[i].move_delay--;
10958
10959     /* due to bugs in previous versions, counter must count up, not down */
10960     if (stored_player[i].push_delay != -1)
10961       stored_player[i].push_delay++;
10962
10963     if (stored_player[i].drop_delay > 0)
10964       stored_player[i].drop_delay--;
10965
10966     if (stored_player[i].is_dropping_pressed)
10967       stored_player[i].drop_pressed_delay++;
10968   }
10969 }
10970
10971 void StartGameActions(boolean init_network_game, boolean record_tape,
10972                       long random_seed)
10973 {
10974   unsigned long new_random_seed = InitRND(random_seed);
10975
10976   if (record_tape)
10977     TapeStartRecording(new_random_seed);
10978
10979 #if defined(NETWORK_AVALIABLE)
10980   if (init_network_game)
10981   {
10982     SendToServer_StartPlaying();
10983
10984     return;
10985   }
10986 #endif
10987
10988   InitGame();
10989 }
10990
10991 void GameActions()
10992 {
10993   static unsigned long game_frame_delay = 0;
10994   unsigned long game_frame_delay_value;
10995   byte *recorded_player_action;
10996   byte summarized_player_action = 0;
10997   byte tape_action[MAX_PLAYERS];
10998   int i;
10999
11000   /* detect endless loops, caused by custom element programming */
11001   if (recursion_loop_detected && recursion_loop_depth == 0)
11002   {
11003     char *message = getStringCat3("Internal Error ! Element ",
11004                                   EL_NAME(recursion_loop_element),
11005                                   " caused endless loop ! Quit the game ?");
11006
11007     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11008           EL_NAME(recursion_loop_element));
11009
11010     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11011
11012     recursion_loop_detected = FALSE;    /* if game should be continued */
11013
11014     free(message);
11015
11016     return;
11017   }
11018
11019   if (game.restart_level)
11020     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11021
11022   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11023   {
11024     if (level.native_em_level->lev->home == 0)  /* all players at home */
11025     {
11026       PlayerWins(local_player);
11027
11028       AllPlayersGone = TRUE;
11029
11030       level.native_em_level->lev->home = -1;
11031     }
11032
11033     if (level.native_em_level->ply[0]->alive == 0 &&
11034         level.native_em_level->ply[1]->alive == 0 &&
11035         level.native_em_level->ply[2]->alive == 0 &&
11036         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11037       AllPlayersGone = TRUE;
11038   }
11039
11040   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11041     GameWon();
11042
11043   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11044     TapeStop();
11045
11046   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11047     return;
11048
11049   game_frame_delay_value =
11050     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11051
11052   if (tape.playing && tape.warp_forward && !tape.pausing)
11053     game_frame_delay_value = 0;
11054
11055   /* ---------- main game synchronization point ---------- */
11056
11057   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11058
11059   if (network_playing && !network_player_action_received)
11060   {
11061     /* try to get network player actions in time */
11062
11063 #if defined(NETWORK_AVALIABLE)
11064     /* last chance to get network player actions without main loop delay */
11065     HandleNetworking();
11066 #endif
11067
11068     /* game was quit by network peer */
11069     if (game_status != GAME_MODE_PLAYING)
11070       return;
11071
11072     if (!network_player_action_received)
11073       return;           /* failed to get network player actions in time */
11074
11075     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11076   }
11077
11078   if (tape.pausing)
11079     return;
11080
11081   /* at this point we know that we really continue executing the game */
11082
11083   network_player_action_received = FALSE;
11084
11085   /* when playing tape, read previously recorded player input from tape data */
11086   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11087
11088 #if 1
11089   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11090   if (tape.pausing)
11091     return;
11092 #endif
11093
11094   if (tape.set_centered_player)
11095   {
11096     game.centered_player_nr_next = tape.centered_player_nr_next;
11097     game.set_centered_player = TRUE;
11098   }
11099
11100   for (i = 0; i < MAX_PLAYERS; i++)
11101   {
11102     summarized_player_action |= stored_player[i].action;
11103
11104     if (!network_playing)
11105       stored_player[i].effective_action = stored_player[i].action;
11106   }
11107
11108 #if defined(NETWORK_AVALIABLE)
11109   if (network_playing)
11110     SendToServer_MovePlayer(summarized_player_action);
11111 #endif
11112
11113   if (!options.network && !setup.team_mode)
11114     local_player->effective_action = summarized_player_action;
11115
11116   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11117   {
11118     for (i = 0; i < MAX_PLAYERS; i++)
11119       stored_player[i].effective_action =
11120         (i == game.centered_player_nr ? summarized_player_action : 0);
11121   }
11122
11123   if (recorded_player_action != NULL)
11124     for (i = 0; i < MAX_PLAYERS; i++)
11125       stored_player[i].effective_action = recorded_player_action[i];
11126
11127   for (i = 0; i < MAX_PLAYERS; i++)
11128   {
11129     tape_action[i] = stored_player[i].effective_action;
11130
11131     /* (this can only happen in the R'n'D game engine) */
11132     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11133       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11134   }
11135
11136   /* only record actions from input devices, but not programmed actions */
11137   if (tape.recording)
11138     TapeRecordAction(tape_action);
11139
11140   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11141   {
11142     GameActions_EM_Main();
11143   }
11144   else
11145   {
11146     GameActions_RND();
11147   }
11148 }
11149
11150 void GameActions_EM_Main()
11151 {
11152   byte effective_action[MAX_PLAYERS];
11153   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11154   int i;
11155
11156   for (i = 0; i < MAX_PLAYERS; i++)
11157     effective_action[i] = stored_player[i].effective_action;
11158
11159   GameActions_EM(effective_action, warp_mode);
11160
11161   CheckLevelTime();
11162
11163   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11164 }
11165
11166 void GameActions_RND()
11167 {
11168   int magic_wall_x = 0, magic_wall_y = 0;
11169   int i, x, y, element, graphic;
11170
11171   InitPlayfieldScanModeVars();
11172
11173 #if USE_ONE_MORE_CHANGE_PER_FRAME
11174   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11175   {
11176     SCAN_PLAYFIELD(x, y)
11177     {
11178       ChangeCount[x][y] = 0;
11179       ChangeEvent[x][y] = -1;
11180     }
11181   }
11182 #endif
11183
11184   if (game.set_centered_player)
11185   {
11186     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11187
11188     /* switching to "all players" only possible if all players fit to screen */
11189     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11190     {
11191       game.centered_player_nr_next = game.centered_player_nr;
11192       game.set_centered_player = FALSE;
11193     }
11194
11195     /* do not switch focus to non-existing (or non-active) player */
11196     if (game.centered_player_nr_next >= 0 &&
11197         !stored_player[game.centered_player_nr_next].active)
11198     {
11199       game.centered_player_nr_next = game.centered_player_nr;
11200       game.set_centered_player = FALSE;
11201     }
11202   }
11203
11204   if (game.set_centered_player &&
11205       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11206   {
11207     int sx, sy;
11208
11209     if (game.centered_player_nr_next == -1)
11210     {
11211       setScreenCenteredToAllPlayers(&sx, &sy);
11212     }
11213     else
11214     {
11215       sx = stored_player[game.centered_player_nr_next].jx;
11216       sy = stored_player[game.centered_player_nr_next].jy;
11217     }
11218
11219     game.centered_player_nr = game.centered_player_nr_next;
11220     game.set_centered_player = FALSE;
11221
11222     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11223     DrawGameDoorValues();
11224   }
11225
11226   for (i = 0; i < MAX_PLAYERS; i++)
11227   {
11228     int actual_player_action = stored_player[i].effective_action;
11229
11230 #if 1
11231     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11232        - rnd_equinox_tetrachloride 048
11233        - rnd_equinox_tetrachloride_ii 096
11234        - rnd_emanuel_schmieg 002
11235        - doctor_sloan_ww 001, 020
11236     */
11237     if (stored_player[i].MovPos == 0)
11238       CheckGravityMovement(&stored_player[i]);
11239 #endif
11240
11241     /* overwrite programmed action with tape action */
11242     if (stored_player[i].programmed_action)
11243       actual_player_action = stored_player[i].programmed_action;
11244
11245     PlayerActions(&stored_player[i], actual_player_action);
11246
11247     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11248   }
11249
11250   ScrollScreen(NULL, SCROLL_GO_ON);
11251
11252   /* for backwards compatibility, the following code emulates a fixed bug that
11253      occured when pushing elements (causing elements that just made their last
11254      pushing step to already (if possible) make their first falling step in the
11255      same game frame, which is bad); this code is also needed to use the famous
11256      "spring push bug" which is used in older levels and might be wanted to be
11257      used also in newer levels, but in this case the buggy pushing code is only
11258      affecting the "spring" element and no other elements */
11259
11260   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11261   {
11262     for (i = 0; i < MAX_PLAYERS; i++)
11263     {
11264       struct PlayerInfo *player = &stored_player[i];
11265       int x = player->jx;
11266       int y = player->jy;
11267
11268       if (player->active && player->is_pushing && player->is_moving &&
11269           IS_MOVING(x, y) &&
11270           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11271            Feld[x][y] == EL_SPRING))
11272       {
11273         ContinueMoving(x, y);
11274
11275         /* continue moving after pushing (this is actually a bug) */
11276         if (!IS_MOVING(x, y))
11277           Stop[x][y] = FALSE;
11278       }
11279     }
11280   }
11281
11282 #if 0
11283   debug_print_timestamp(0, "start main loop profiling");
11284 #endif
11285
11286   SCAN_PLAYFIELD(x, y)
11287   {
11288     ChangeCount[x][y] = 0;
11289     ChangeEvent[x][y] = -1;
11290
11291     /* this must be handled before main playfield loop */
11292     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11293     {
11294       MovDelay[x][y]--;
11295       if (MovDelay[x][y] <= 0)
11296         RemoveField(x, y);
11297     }
11298
11299 #if USE_NEW_SNAP_DELAY
11300     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11301     {
11302       MovDelay[x][y]--;
11303       if (MovDelay[x][y] <= 0)
11304       {
11305         RemoveField(x, y);
11306         DrawLevelField(x, y);
11307
11308         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11309       }
11310     }
11311 #endif
11312
11313 #if DEBUG
11314     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11315     {
11316       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11317       printf("GameActions(): This should never happen!\n");
11318
11319       ChangePage[x][y] = -1;
11320     }
11321 #endif
11322
11323     Stop[x][y] = FALSE;
11324     if (WasJustMoving[x][y] > 0)
11325       WasJustMoving[x][y]--;
11326     if (WasJustFalling[x][y] > 0)
11327       WasJustFalling[x][y]--;
11328     if (CheckCollision[x][y] > 0)
11329       CheckCollision[x][y]--;
11330     if (CheckImpact[x][y] > 0)
11331       CheckImpact[x][y]--;
11332
11333     GfxFrame[x][y]++;
11334
11335     /* reset finished pushing action (not done in ContinueMoving() to allow
11336        continuous pushing animation for elements with zero push delay) */
11337     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11338     {
11339       ResetGfxAnimation(x, y);
11340       DrawLevelField(x, y);
11341     }
11342
11343 #if DEBUG
11344     if (IS_BLOCKED(x, y))
11345     {
11346       int oldx, oldy;
11347
11348       Blocked2Moving(x, y, &oldx, &oldy);
11349       if (!IS_MOVING(oldx, oldy))
11350       {
11351         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11352         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11353         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11354         printf("GameActions(): This should never happen!\n");
11355       }
11356     }
11357 #endif
11358   }
11359
11360 #if 0
11361   debug_print_timestamp(0, "- time for pre-main loop:");
11362 #endif
11363
11364 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11365   SCAN_PLAYFIELD(x, y)
11366   {
11367     element = Feld[x][y];
11368     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11369
11370 #if 1
11371     {
11372 #if 1
11373       int element2 = element;
11374       int graphic2 = graphic;
11375 #else
11376       int element2 = Feld[x][y];
11377       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11378 #endif
11379       int last_gfx_frame = GfxFrame[x][y];
11380
11381       if (graphic_info[graphic2].anim_global_sync)
11382         GfxFrame[x][y] = FrameCounter;
11383       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11384         GfxFrame[x][y] = CustomValue[x][y];
11385       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11386         GfxFrame[x][y] = element_info[element2].collect_score;
11387       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11388         GfxFrame[x][y] = ChangeDelay[x][y];
11389
11390       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11391         DrawLevelGraphicAnimation(x, y, graphic2);
11392     }
11393 #else
11394     ResetGfxFrame(x, y, TRUE);
11395 #endif
11396
11397 #if 1
11398     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11399         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11400       ResetRandomAnimationValue(x, y);
11401 #endif
11402
11403 #if 1
11404     SetRandomAnimationValue(x, y);
11405 #endif
11406
11407 #if 1
11408     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11409 #endif
11410   }
11411 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11412
11413 #if 0
11414   debug_print_timestamp(0, "- time for TEST loop:     -->");
11415 #endif
11416
11417   SCAN_PLAYFIELD(x, y)
11418   {
11419     element = Feld[x][y];
11420     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11421
11422     ResetGfxFrame(x, y, TRUE);
11423
11424     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11425         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11426       ResetRandomAnimationValue(x, y);
11427
11428     SetRandomAnimationValue(x, y);
11429
11430     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11431
11432     if (IS_INACTIVE(element))
11433     {
11434       if (IS_ANIMATED(graphic))
11435         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11436
11437       continue;
11438     }
11439
11440     /* this may take place after moving, so 'element' may have changed */
11441     if (IS_CHANGING(x, y) &&
11442         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11443     {
11444       int page = element_info[element].event_page_nr[CE_DELAY];
11445
11446 #if 1
11447       HandleElementChange(x, y, page);
11448 #else
11449       if (CAN_CHANGE(element))
11450         HandleElementChange(x, y, page);
11451
11452       if (HAS_ACTION(element))
11453         ExecuteCustomElementAction(x, y, element, page);
11454 #endif
11455
11456       element = Feld[x][y];
11457       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11458     }
11459
11460 #if 0   // ---------------------------------------------------------------------
11461
11462     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11463     {
11464       StartMoving(x, y);
11465
11466       element = Feld[x][y];
11467       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11468
11469       if (IS_ANIMATED(graphic) &&
11470           !IS_MOVING(x, y) &&
11471           !Stop[x][y])
11472         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11473
11474       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11475         DrawTwinkleOnField(x, y);
11476     }
11477     else if (IS_MOVING(x, y))
11478       ContinueMoving(x, y);
11479     else
11480     {
11481       switch (element)
11482       {
11483         case EL_ACID:
11484         case EL_EXIT_OPEN:
11485         case EL_EM_EXIT_OPEN:
11486         case EL_SP_EXIT_OPEN:
11487         case EL_STEEL_EXIT_OPEN:
11488         case EL_EM_STEEL_EXIT_OPEN:
11489         case EL_SP_TERMINAL:
11490         case EL_SP_TERMINAL_ACTIVE:
11491         case EL_EXTRA_TIME:
11492         case EL_SHIELD_NORMAL:
11493         case EL_SHIELD_DEADLY:
11494           if (IS_ANIMATED(graphic))
11495             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11496           break;
11497
11498         case EL_DYNAMITE_ACTIVE:
11499         case EL_EM_DYNAMITE_ACTIVE:
11500         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11501         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11502         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11503         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11504         case EL_SP_DISK_RED_ACTIVE:
11505           CheckDynamite(x, y);
11506           break;
11507
11508         case EL_AMOEBA_GROWING:
11509           AmoebeWaechst(x, y);
11510           break;
11511
11512         case EL_AMOEBA_SHRINKING:
11513           AmoebaDisappearing(x, y);
11514           break;
11515
11516 #if !USE_NEW_AMOEBA_CODE
11517         case EL_AMOEBA_WET:
11518         case EL_AMOEBA_DRY:
11519         case EL_AMOEBA_FULL:
11520         case EL_BD_AMOEBA:
11521         case EL_EMC_DRIPPER:
11522           AmoebeAbleger(x, y);
11523           break;
11524 #endif
11525
11526         case EL_GAME_OF_LIFE:
11527         case EL_BIOMAZE:
11528           Life(x, y);
11529           break;
11530
11531         case EL_EXIT_CLOSED:
11532           CheckExit(x, y);
11533           break;
11534
11535         case EL_EM_EXIT_CLOSED:
11536           CheckExitEM(x, y);
11537           break;
11538
11539         case EL_STEEL_EXIT_CLOSED:
11540           CheckExitSteel(x, y);
11541           break;
11542
11543         case EL_EM_STEEL_EXIT_CLOSED:
11544           CheckExitSteelEM(x, y);
11545           break;
11546
11547         case EL_SP_EXIT_CLOSED:
11548           CheckExitSP(x, y);
11549           break;
11550
11551         case EL_EXPANDABLE_WALL_GROWING:
11552         case EL_EXPANDABLE_STEELWALL_GROWING:
11553           MauerWaechst(x, y);
11554           break;
11555
11556         case EL_EXPANDABLE_WALL:
11557         case EL_EXPANDABLE_WALL_HORIZONTAL:
11558         case EL_EXPANDABLE_WALL_VERTICAL:
11559         case EL_EXPANDABLE_WALL_ANY:
11560         case EL_BD_EXPANDABLE_WALL:
11561           MauerAbleger(x, y);
11562           break;
11563
11564         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11565         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11566         case EL_EXPANDABLE_STEELWALL_ANY:
11567           MauerAblegerStahl(x, y);
11568           break;
11569
11570         case EL_FLAMES:
11571           CheckForDragon(x, y);
11572           break;
11573
11574         case EL_EXPLOSION:
11575           break;
11576
11577         case EL_ELEMENT_SNAPPING:
11578         case EL_DIAGONAL_SHRINKING:
11579         case EL_DIAGONAL_GROWING:
11580         {
11581           graphic =
11582             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11583
11584           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11585           break;
11586         }
11587
11588         default:
11589           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11590             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11591           break;
11592       }
11593     }
11594
11595 #else   // ---------------------------------------------------------------------
11596
11597     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11598     {
11599       StartMoving(x, y);
11600
11601       element = Feld[x][y];
11602       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11603
11604       if (IS_ANIMATED(graphic) &&
11605           !IS_MOVING(x, y) &&
11606           !Stop[x][y])
11607         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11608
11609       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11610         DrawTwinkleOnField(x, y);
11611     }
11612     else if ((element == EL_ACID ||
11613               element == EL_EXIT_OPEN ||
11614               element == EL_EM_EXIT_OPEN ||
11615               element == EL_SP_EXIT_OPEN ||
11616               element == EL_STEEL_EXIT_OPEN ||
11617               element == EL_EM_STEEL_EXIT_OPEN ||
11618               element == EL_SP_TERMINAL ||
11619               element == EL_SP_TERMINAL_ACTIVE ||
11620               element == EL_EXTRA_TIME ||
11621               element == EL_SHIELD_NORMAL ||
11622               element == EL_SHIELD_DEADLY) &&
11623              IS_ANIMATED(graphic))
11624       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11625     else if (IS_MOVING(x, y))
11626       ContinueMoving(x, y);
11627     else if (IS_ACTIVE_BOMB(element))
11628       CheckDynamite(x, y);
11629     else if (element == EL_AMOEBA_GROWING)
11630       AmoebeWaechst(x, y);
11631     else if (element == EL_AMOEBA_SHRINKING)
11632       AmoebaDisappearing(x, y);
11633
11634 #if !USE_NEW_AMOEBA_CODE
11635     else if (IS_AMOEBALIVE(element))
11636       AmoebeAbleger(x, y);
11637 #endif
11638
11639     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11640       Life(x, y);
11641     else if (element == EL_EXIT_CLOSED)
11642       CheckExit(x, y);
11643     else if (element == EL_EM_EXIT_CLOSED)
11644       CheckExitEM(x, y);
11645     else if (element == EL_STEEL_EXIT_CLOSED)
11646       CheckExitSteel(x, y);
11647     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11648       CheckExitSteelEM(x, y);
11649     else if (element == EL_SP_EXIT_CLOSED)
11650       CheckExitSP(x, y);
11651     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11652              element == EL_EXPANDABLE_STEELWALL_GROWING)
11653       MauerWaechst(x, y);
11654     else if (element == EL_EXPANDABLE_WALL ||
11655              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11656              element == EL_EXPANDABLE_WALL_VERTICAL ||
11657              element == EL_EXPANDABLE_WALL_ANY ||
11658              element == EL_BD_EXPANDABLE_WALL)
11659       MauerAbleger(x, y);
11660     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11661              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11662              element == EL_EXPANDABLE_STEELWALL_ANY)
11663       MauerAblegerStahl(x, y);
11664     else if (element == EL_FLAMES)
11665       CheckForDragon(x, y);
11666     else if (element == EL_EXPLOSION)
11667       ; /* drawing of correct explosion animation is handled separately */
11668     else if (element == EL_ELEMENT_SNAPPING ||
11669              element == EL_DIAGONAL_SHRINKING ||
11670              element == EL_DIAGONAL_GROWING)
11671     {
11672       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11673
11674       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11675     }
11676     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11677       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11678
11679 #endif  // ---------------------------------------------------------------------
11680
11681     if (IS_BELT_ACTIVE(element))
11682       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11683
11684     if (game.magic_wall_active)
11685     {
11686       int jx = local_player->jx, jy = local_player->jy;
11687
11688       /* play the element sound at the position nearest to the player */
11689       if ((element == EL_MAGIC_WALL_FULL ||
11690            element == EL_MAGIC_WALL_ACTIVE ||
11691            element == EL_MAGIC_WALL_EMPTYING ||
11692            element == EL_BD_MAGIC_WALL_FULL ||
11693            element == EL_BD_MAGIC_WALL_ACTIVE ||
11694            element == EL_BD_MAGIC_WALL_EMPTYING ||
11695            element == EL_DC_MAGIC_WALL_FULL ||
11696            element == EL_DC_MAGIC_WALL_ACTIVE ||
11697            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11698           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11699       {
11700         magic_wall_x = x;
11701         magic_wall_y = y;
11702       }
11703     }
11704   }
11705
11706 #if 0
11707   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11708 #endif
11709
11710 #if USE_NEW_AMOEBA_CODE
11711   /* new experimental amoeba growth stuff */
11712   if (!(FrameCounter % 8))
11713   {
11714     static unsigned long random = 1684108901;
11715
11716     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11717     {
11718       x = RND(lev_fieldx);
11719       y = RND(lev_fieldy);
11720       element = Feld[x][y];
11721
11722       if (!IS_PLAYER(x,y) &&
11723           (element == EL_EMPTY ||
11724            CAN_GROW_INTO(element) ||
11725            element == EL_QUICKSAND_EMPTY ||
11726            element == EL_QUICKSAND_FAST_EMPTY ||
11727            element == EL_ACID_SPLASH_LEFT ||
11728            element == EL_ACID_SPLASH_RIGHT))
11729       {
11730         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11731             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11732             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11733             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11734           Feld[x][y] = EL_AMOEBA_DROP;
11735       }
11736
11737       random = random * 129 + 1;
11738     }
11739   }
11740 #endif
11741
11742 #if 0
11743   if (game.explosions_delayed)
11744 #endif
11745   {
11746     game.explosions_delayed = FALSE;
11747
11748     SCAN_PLAYFIELD(x, y)
11749     {
11750       element = Feld[x][y];
11751
11752       if (ExplodeField[x][y])
11753         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11754       else if (element == EL_EXPLOSION)
11755         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11756
11757       ExplodeField[x][y] = EX_TYPE_NONE;
11758     }
11759
11760     game.explosions_delayed = TRUE;
11761   }
11762
11763   if (game.magic_wall_active)
11764   {
11765     if (!(game.magic_wall_time_left % 4))
11766     {
11767       int element = Feld[magic_wall_x][magic_wall_y];
11768
11769       if (element == EL_BD_MAGIC_WALL_FULL ||
11770           element == EL_BD_MAGIC_WALL_ACTIVE ||
11771           element == EL_BD_MAGIC_WALL_EMPTYING)
11772         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11773       else if (element == EL_DC_MAGIC_WALL_FULL ||
11774                element == EL_DC_MAGIC_WALL_ACTIVE ||
11775                element == EL_DC_MAGIC_WALL_EMPTYING)
11776         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11777       else
11778         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11779     }
11780
11781     if (game.magic_wall_time_left > 0)
11782     {
11783       game.magic_wall_time_left--;
11784
11785       if (!game.magic_wall_time_left)
11786       {
11787         SCAN_PLAYFIELD(x, y)
11788         {
11789           element = Feld[x][y];
11790
11791           if (element == EL_MAGIC_WALL_ACTIVE ||
11792               element == EL_MAGIC_WALL_FULL)
11793           {
11794             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11795             DrawLevelField(x, y);
11796           }
11797           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11798                    element == EL_BD_MAGIC_WALL_FULL)
11799           {
11800             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11801             DrawLevelField(x, y);
11802           }
11803           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11804                    element == EL_DC_MAGIC_WALL_FULL)
11805           {
11806             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11807             DrawLevelField(x, y);
11808           }
11809         }
11810
11811         game.magic_wall_active = FALSE;
11812       }
11813     }
11814   }
11815
11816   if (game.light_time_left > 0)
11817   {
11818     game.light_time_left--;
11819
11820     if (game.light_time_left == 0)
11821       RedrawAllLightSwitchesAndInvisibleElements();
11822   }
11823
11824   if (game.timegate_time_left > 0)
11825   {
11826     game.timegate_time_left--;
11827
11828     if (game.timegate_time_left == 0)
11829       CloseAllOpenTimegates();
11830   }
11831
11832   if (game.lenses_time_left > 0)
11833   {
11834     game.lenses_time_left--;
11835
11836     if (game.lenses_time_left == 0)
11837       RedrawAllInvisibleElementsForLenses();
11838   }
11839
11840   if (game.magnify_time_left > 0)
11841   {
11842     game.magnify_time_left--;
11843
11844     if (game.magnify_time_left == 0)
11845       RedrawAllInvisibleElementsForMagnifier();
11846   }
11847
11848   for (i = 0; i < MAX_PLAYERS; i++)
11849   {
11850     struct PlayerInfo *player = &stored_player[i];
11851
11852     if (SHIELD_ON(player))
11853     {
11854       if (player->shield_deadly_time_left)
11855         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11856       else if (player->shield_normal_time_left)
11857         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11858     }
11859   }
11860
11861   CheckLevelTime();
11862
11863   DrawAllPlayers();
11864   PlayAllPlayersSound();
11865
11866   if (options.debug)                    /* calculate frames per second */
11867   {
11868     static unsigned long fps_counter = 0;
11869     static int fps_frames = 0;
11870     unsigned long fps_delay_ms = Counter() - fps_counter;
11871
11872     fps_frames++;
11873
11874     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11875     {
11876       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11877
11878       fps_frames = 0;
11879       fps_counter = Counter();
11880     }
11881
11882     redraw_mask |= REDRAW_FPS;
11883   }
11884
11885   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11886
11887   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11888   {
11889     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11890
11891     local_player->show_envelope = 0;
11892   }
11893
11894 #if 0
11895   debug_print_timestamp(0, "stop main loop profiling ");
11896   printf("----------------------------------------------------------\n");
11897 #endif
11898
11899   /* use random number generator in every frame to make it less predictable */
11900   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11901     RND(1);
11902 }
11903
11904 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11905 {
11906   int min_x = x, min_y = y, max_x = x, max_y = y;
11907   int i;
11908
11909   for (i = 0; i < MAX_PLAYERS; i++)
11910   {
11911     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11912
11913     if (!stored_player[i].active || &stored_player[i] == player)
11914       continue;
11915
11916     min_x = MIN(min_x, jx);
11917     min_y = MIN(min_y, jy);
11918     max_x = MAX(max_x, jx);
11919     max_y = MAX(max_y, jy);
11920   }
11921
11922   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11923 }
11924
11925 static boolean AllPlayersInVisibleScreen()
11926 {
11927   int i;
11928
11929   for (i = 0; i < MAX_PLAYERS; i++)
11930   {
11931     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11932
11933     if (!stored_player[i].active)
11934       continue;
11935
11936     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11937       return FALSE;
11938   }
11939
11940   return TRUE;
11941 }
11942
11943 void ScrollLevel(int dx, int dy)
11944 {
11945 #if 1
11946   static Bitmap *bitmap_db_field2 = NULL;
11947   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
11948   int x, y;
11949 #else
11950   int i, x, y;
11951 #endif
11952
11953 #if 0
11954   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
11955   /* only horizontal XOR vertical scroll direction allowed */
11956   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
11957     return;
11958 #endif
11959
11960 #if 1
11961   if (bitmap_db_field2 == NULL)
11962     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
11963
11964   /* needed when blitting directly to same bitmap -- should not be needed with
11965      recent SDL libraries, but apparently does not work in 1.2.11 directly */
11966   BlitBitmap(drawto_field, bitmap_db_field2,
11967              FX + TILEX * (dx == -1) - softscroll_offset,
11968              FY + TILEY * (dy == -1) - softscroll_offset,
11969              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11970              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11971              FX + TILEX * (dx == 1) - softscroll_offset,
11972              FY + TILEY * (dy == 1) - softscroll_offset);
11973   BlitBitmap(bitmap_db_field2, drawto_field,
11974              FX + TILEX * (dx == 1) - softscroll_offset,
11975              FY + TILEY * (dy == 1) - softscroll_offset,
11976              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
11977              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
11978              FX + TILEX * (dx == 1) - softscroll_offset,
11979              FY + TILEY * (dy == 1) - softscroll_offset);
11980
11981 #else
11982
11983 #if 1
11984   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
11985   int xsize = (BX2 - BX1 + 1);
11986   int ysize = (BY2 - BY1 + 1);
11987   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
11988   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
11989   int step  = (start < end ? +1 : -1);
11990
11991   for (i = start; i != end; i += step)
11992   {
11993     BlitBitmap(drawto_field, drawto_field,
11994                FX + TILEX * (dx != 0 ? i + step : 0),
11995                FY + TILEY * (dy != 0 ? i + step : 0),
11996                TILEX * (dx != 0 ? 1 : xsize),
11997                TILEY * (dy != 0 ? 1 : ysize),
11998                FX + TILEX * (dx != 0 ? i : 0),
11999                FY + TILEY * (dy != 0 ? i : 0));
12000   }
12001
12002 #else
12003
12004   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12005
12006   BlitBitmap(drawto_field, drawto_field,
12007              FX + TILEX * (dx == -1) - softscroll_offset,
12008              FY + TILEY * (dy == -1) - softscroll_offset,
12009              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12010              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12011              FX + TILEX * (dx == 1) - softscroll_offset,
12012              FY + TILEY * (dy == 1) - softscroll_offset);
12013 #endif
12014 #endif
12015
12016   if (dx != 0)
12017   {
12018     x = (dx == 1 ? BX1 : BX2);
12019     for (y = BY1; y <= BY2; y++)
12020       DrawScreenField(x, y);
12021   }
12022
12023   if (dy != 0)
12024   {
12025     y = (dy == 1 ? BY1 : BY2);
12026     for (x = BX1; x <= BX2; x++)
12027       DrawScreenField(x, y);
12028   }
12029
12030   redraw_mask |= REDRAW_FIELD;
12031 }
12032
12033 static boolean canFallDown(struct PlayerInfo *player)
12034 {
12035   int jx = player->jx, jy = player->jy;
12036
12037   return (IN_LEV_FIELD(jx, jy + 1) &&
12038           (IS_FREE(jx, jy + 1) ||
12039            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12040           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12041           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12042 }
12043
12044 static boolean canPassField(int x, int y, int move_dir)
12045 {
12046   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12047   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12048   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12049   int nextx = x + dx;
12050   int nexty = y + dy;
12051   int element = Feld[x][y];
12052
12053   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12054           !CAN_MOVE(element) &&
12055           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12056           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12057           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12058 }
12059
12060 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12061 {
12062   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12063   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12064   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12065   int newx = x + dx;
12066   int newy = y + dy;
12067
12068   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12069           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12070           (IS_DIGGABLE(Feld[newx][newy]) ||
12071            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12072            canPassField(newx, newy, move_dir)));
12073 }
12074
12075 static void CheckGravityMovement(struct PlayerInfo *player)
12076 {
12077 #if USE_PLAYER_GRAVITY
12078   if (player->gravity && !player->programmed_action)
12079 #else
12080   if (game.gravity && !player->programmed_action)
12081 #endif
12082   {
12083     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12084     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12085     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12086     int jx = player->jx, jy = player->jy;
12087     boolean player_is_moving_to_valid_field =
12088       (!player_is_snapping &&
12089        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12090         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12091     boolean player_can_fall_down = canFallDown(player);
12092
12093     if (player_can_fall_down &&
12094         !player_is_moving_to_valid_field)
12095       player->programmed_action = MV_DOWN;
12096   }
12097 }
12098
12099 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12100 {
12101   return CheckGravityMovement(player);
12102
12103 #if USE_PLAYER_GRAVITY
12104   if (player->gravity && !player->programmed_action)
12105 #else
12106   if (game.gravity && !player->programmed_action)
12107 #endif
12108   {
12109     int jx = player->jx, jy = player->jy;
12110     boolean field_under_player_is_free =
12111       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12112     boolean player_is_standing_on_valid_field =
12113       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12114        (IS_WALKABLE(Feld[jx][jy]) &&
12115         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12116
12117     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12118       player->programmed_action = MV_DOWN;
12119   }
12120 }
12121
12122 /*
12123   MovePlayerOneStep()
12124   -----------------------------------------------------------------------------
12125   dx, dy:               direction (non-diagonal) to try to move the player to
12126   real_dx, real_dy:     direction as read from input device (can be diagonal)
12127 */
12128
12129 boolean MovePlayerOneStep(struct PlayerInfo *player,
12130                           int dx, int dy, int real_dx, int real_dy)
12131 {
12132   int jx = player->jx, jy = player->jy;
12133   int new_jx = jx + dx, new_jy = jy + dy;
12134 #if !USE_FIXED_DONT_RUN_INTO
12135   int element;
12136 #endif
12137   int can_move;
12138   boolean player_can_move = !player->cannot_move;
12139
12140   if (!player->active || (!dx && !dy))
12141     return MP_NO_ACTION;
12142
12143   player->MovDir = (dx < 0 ? MV_LEFT :
12144                     dx > 0 ? MV_RIGHT :
12145                     dy < 0 ? MV_UP :
12146                     dy > 0 ? MV_DOWN :  MV_NONE);
12147
12148   if (!IN_LEV_FIELD(new_jx, new_jy))
12149     return MP_NO_ACTION;
12150
12151   if (!player_can_move)
12152   {
12153     if (player->MovPos == 0)
12154     {
12155       player->is_moving = FALSE;
12156       player->is_digging = FALSE;
12157       player->is_collecting = FALSE;
12158       player->is_snapping = FALSE;
12159       player->is_pushing = FALSE;
12160     }
12161   }
12162
12163 #if 1
12164   if (!options.network && game.centered_player_nr == -1 &&
12165       !AllPlayersInSight(player, new_jx, new_jy))
12166     return MP_NO_ACTION;
12167 #else
12168   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12169     return MP_NO_ACTION;
12170 #endif
12171
12172 #if !USE_FIXED_DONT_RUN_INTO
12173   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12174
12175   /* (moved to DigField()) */
12176   if (player_can_move && DONT_RUN_INTO(element))
12177   {
12178     if (element == EL_ACID && dx == 0 && dy == 1)
12179     {
12180       SplashAcid(new_jx, new_jy);
12181       Feld[jx][jy] = EL_PLAYER_1;
12182       InitMovingField(jx, jy, MV_DOWN);
12183       Store[jx][jy] = EL_ACID;
12184       ContinueMoving(jx, jy);
12185       BuryPlayer(player);
12186     }
12187     else
12188       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12189
12190     return MP_MOVING;
12191   }
12192 #endif
12193
12194   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12195   if (can_move != MP_MOVING)
12196     return can_move;
12197
12198   /* check if DigField() has caused relocation of the player */
12199   if (player->jx != jx || player->jy != jy)
12200     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12201
12202   StorePlayer[jx][jy] = 0;
12203   player->last_jx = jx;
12204   player->last_jy = jy;
12205   player->jx = new_jx;
12206   player->jy = new_jy;
12207   StorePlayer[new_jx][new_jy] = player->element_nr;
12208
12209   if (player->move_delay_value_next != -1)
12210   {
12211     player->move_delay_value = player->move_delay_value_next;
12212     player->move_delay_value_next = -1;
12213   }
12214
12215   player->MovPos =
12216     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12217
12218   player->step_counter++;
12219
12220   PlayerVisit[jx][jy] = FrameCounter;
12221
12222 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12223   player->is_moving = TRUE;
12224 #endif
12225
12226 #if 1
12227   /* should better be called in MovePlayer(), but this breaks some tapes */
12228   ScrollPlayer(player, SCROLL_INIT);
12229 #endif
12230
12231   return MP_MOVING;
12232 }
12233
12234 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12235 {
12236   int jx = player->jx, jy = player->jy;
12237   int old_jx = jx, old_jy = jy;
12238   int moved = MP_NO_ACTION;
12239
12240   if (!player->active)
12241     return FALSE;
12242
12243   if (!dx && !dy)
12244   {
12245     if (player->MovPos == 0)
12246     {
12247       player->is_moving = FALSE;
12248       player->is_digging = FALSE;
12249       player->is_collecting = FALSE;
12250       player->is_snapping = FALSE;
12251       player->is_pushing = FALSE;
12252     }
12253
12254     return FALSE;
12255   }
12256
12257   if (player->move_delay > 0)
12258     return FALSE;
12259
12260   player->move_delay = -1;              /* set to "uninitialized" value */
12261
12262   /* store if player is automatically moved to next field */
12263   player->is_auto_moving = (player->programmed_action != MV_NONE);
12264
12265   /* remove the last programmed player action */
12266   player->programmed_action = 0;
12267
12268   if (player->MovPos)
12269   {
12270     /* should only happen if pre-1.2 tape recordings are played */
12271     /* this is only for backward compatibility */
12272
12273     int original_move_delay_value = player->move_delay_value;
12274
12275 #if DEBUG
12276     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12277            tape.counter);
12278 #endif
12279
12280     /* scroll remaining steps with finest movement resolution */
12281     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12282
12283     while (player->MovPos)
12284     {
12285       ScrollPlayer(player, SCROLL_GO_ON);
12286       ScrollScreen(NULL, SCROLL_GO_ON);
12287
12288       AdvanceFrameAndPlayerCounters(player->index_nr);
12289
12290       DrawAllPlayers();
12291       BackToFront();
12292     }
12293
12294     player->move_delay_value = original_move_delay_value;
12295   }
12296
12297   player->is_active = FALSE;
12298
12299   if (player->last_move_dir & MV_HORIZONTAL)
12300   {
12301     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12302       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12303   }
12304   else
12305   {
12306     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12307       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12308   }
12309
12310 #if USE_FIXED_BORDER_RUNNING_GFX
12311   if (!moved && !player->is_active)
12312   {
12313     player->is_moving = FALSE;
12314     player->is_digging = FALSE;
12315     player->is_collecting = FALSE;
12316     player->is_snapping = FALSE;
12317     player->is_pushing = FALSE;
12318   }
12319 #endif
12320
12321   jx = player->jx;
12322   jy = player->jy;
12323
12324 #if 1
12325   if (moved & MP_MOVING && !ScreenMovPos &&
12326       (player->index_nr == game.centered_player_nr ||
12327        game.centered_player_nr == -1))
12328 #else
12329   if (moved & MP_MOVING && !ScreenMovPos &&
12330       (player == local_player || !options.network))
12331 #endif
12332   {
12333     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12334     int offset = game.scroll_delay_value;
12335
12336     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12337     {
12338       /* actual player has left the screen -- scroll in that direction */
12339       if (jx != old_jx)         /* player has moved horizontally */
12340         scroll_x += (jx - old_jx);
12341       else                      /* player has moved vertically */
12342         scroll_y += (jy - old_jy);
12343     }
12344     else
12345     {
12346       if (jx != old_jx)         /* player has moved horizontally */
12347       {
12348         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12349             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12350           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12351
12352         /* don't scroll over playfield boundaries */
12353         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12354           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12355
12356         /* don't scroll more than one field at a time */
12357         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12358
12359         /* don't scroll against the player's moving direction */
12360         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12361             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12362           scroll_x = old_scroll_x;
12363       }
12364       else                      /* player has moved vertically */
12365       {
12366         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12367             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12368           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12369
12370         /* don't scroll over playfield boundaries */
12371         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12372           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12373
12374         /* don't scroll more than one field at a time */
12375         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12376
12377         /* don't scroll against the player's moving direction */
12378         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12379             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12380           scroll_y = old_scroll_y;
12381       }
12382     }
12383
12384     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12385     {
12386 #if 1
12387       if (!options.network && game.centered_player_nr == -1 &&
12388           !AllPlayersInVisibleScreen())
12389       {
12390         scroll_x = old_scroll_x;
12391         scroll_y = old_scroll_y;
12392       }
12393       else
12394 #else
12395       if (!options.network && !AllPlayersInVisibleScreen())
12396       {
12397         scroll_x = old_scroll_x;
12398         scroll_y = old_scroll_y;
12399       }
12400       else
12401 #endif
12402       {
12403         ScrollScreen(player, SCROLL_INIT);
12404         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12405       }
12406     }
12407   }
12408
12409   player->StepFrame = 0;
12410
12411   if (moved & MP_MOVING)
12412   {
12413     if (old_jx != jx && old_jy == jy)
12414       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12415     else if (old_jx == jx && old_jy != jy)
12416       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12417
12418     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12419
12420     player->last_move_dir = player->MovDir;
12421     player->is_moving = TRUE;
12422     player->is_snapping = FALSE;
12423     player->is_switching = FALSE;
12424     player->is_dropping = FALSE;
12425     player->is_dropping_pressed = FALSE;
12426     player->drop_pressed_delay = 0;
12427
12428 #if 0
12429     /* should better be called here than above, but this breaks some tapes */
12430     ScrollPlayer(player, SCROLL_INIT);
12431 #endif
12432   }
12433   else
12434   {
12435     CheckGravityMovementWhenNotMoving(player);
12436
12437     player->is_moving = FALSE;
12438
12439     /* at this point, the player is allowed to move, but cannot move right now
12440        (e.g. because of something blocking the way) -- ensure that the player
12441        is also allowed to move in the next frame (in old versions before 3.1.1,
12442        the player was forced to wait again for eight frames before next try) */
12443
12444     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12445       player->move_delay = 0;   /* allow direct movement in the next frame */
12446   }
12447
12448   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12449     player->move_delay = player->move_delay_value;
12450
12451   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12452   {
12453     TestIfPlayerTouchesBadThing(jx, jy);
12454     TestIfPlayerTouchesCustomElement(jx, jy);
12455   }
12456
12457   if (!player->active)
12458     RemovePlayer(player);
12459
12460   return moved;
12461 }
12462
12463 void ScrollPlayer(struct PlayerInfo *player, int mode)
12464 {
12465   int jx = player->jx, jy = player->jy;
12466   int last_jx = player->last_jx, last_jy = player->last_jy;
12467   int move_stepsize = TILEX / player->move_delay_value;
12468
12469 #if USE_NEW_PLAYER_SPEED
12470   if (!player->active)
12471     return;
12472
12473   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12474     return;
12475 #else
12476   if (!player->active || player->MovPos == 0)
12477     return;
12478 #endif
12479
12480   if (mode == SCROLL_INIT)
12481   {
12482     player->actual_frame_counter = FrameCounter;
12483     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12484
12485     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12486         Feld[last_jx][last_jy] == EL_EMPTY)
12487     {
12488       int last_field_block_delay = 0;   /* start with no blocking at all */
12489       int block_delay_adjustment = player->block_delay_adjustment;
12490
12491       /* if player blocks last field, add delay for exactly one move */
12492       if (player->block_last_field)
12493       {
12494         last_field_block_delay += player->move_delay_value;
12495
12496         /* when blocking enabled, prevent moving up despite gravity */
12497 #if USE_PLAYER_GRAVITY
12498         if (player->gravity && player->MovDir == MV_UP)
12499           block_delay_adjustment = -1;
12500 #else
12501         if (game.gravity && player->MovDir == MV_UP)
12502           block_delay_adjustment = -1;
12503 #endif
12504       }
12505
12506       /* add block delay adjustment (also possible when not blocking) */
12507       last_field_block_delay += block_delay_adjustment;
12508
12509       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12510       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12511     }
12512
12513 #if USE_NEW_PLAYER_SPEED
12514     if (player->MovPos != 0)    /* player has not yet reached destination */
12515       return;
12516 #else
12517     return;
12518 #endif
12519   }
12520   else if (!FrameReached(&player->actual_frame_counter, 1))
12521     return;
12522
12523 #if USE_NEW_PLAYER_SPEED
12524   if (player->MovPos != 0)
12525   {
12526     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12527     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12528
12529     /* before DrawPlayer() to draw correct player graphic for this case */
12530     if (player->MovPos == 0)
12531       CheckGravityMovement(player);
12532   }
12533 #else
12534   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12535   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12536
12537   /* before DrawPlayer() to draw correct player graphic for this case */
12538   if (player->MovPos == 0)
12539     CheckGravityMovement(player);
12540 #endif
12541
12542   if (player->MovPos == 0)      /* player reached destination field */
12543   {
12544     if (player->move_delay_reset_counter > 0)
12545     {
12546       player->move_delay_reset_counter--;
12547
12548       if (player->move_delay_reset_counter == 0)
12549       {
12550         /* continue with normal speed after quickly moving through gate */
12551         HALVE_PLAYER_SPEED(player);
12552
12553         /* be able to make the next move without delay */
12554         player->move_delay = 0;
12555       }
12556     }
12557
12558     player->last_jx = jx;
12559     player->last_jy = jy;
12560
12561     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12562         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12563         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12564         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12565         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12566         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12567     {
12568       DrawPlayer(player);       /* needed here only to cleanup last field */
12569       RemovePlayer(player);
12570
12571       if (local_player->friends_still_needed == 0 ||
12572           IS_SP_ELEMENT(Feld[jx][jy]))
12573         PlayerWins(player);
12574     }
12575
12576     /* this breaks one level: "machine", level 000 */
12577     {
12578       int move_direction = player->MovDir;
12579       int enter_side = MV_DIR_OPPOSITE(move_direction);
12580       int leave_side = move_direction;
12581       int old_jx = last_jx;
12582       int old_jy = last_jy;
12583       int old_element = Feld[old_jx][old_jy];
12584       int new_element = Feld[jx][jy];
12585
12586       if (IS_CUSTOM_ELEMENT(old_element))
12587         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12588                                    CE_LEFT_BY_PLAYER,
12589                                    player->index_bit, leave_side);
12590
12591       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12592                                           CE_PLAYER_LEAVES_X,
12593                                           player->index_bit, leave_side);
12594
12595       if (IS_CUSTOM_ELEMENT(new_element))
12596         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12597                                    player->index_bit, enter_side);
12598
12599       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12600                                           CE_PLAYER_ENTERS_X,
12601                                           player->index_bit, enter_side);
12602
12603       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12604                                         CE_MOVE_OF_X, move_direction);
12605     }
12606
12607     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12608     {
12609       TestIfPlayerTouchesBadThing(jx, jy);
12610       TestIfPlayerTouchesCustomElement(jx, jy);
12611
12612       /* needed because pushed element has not yet reached its destination,
12613          so it would trigger a change event at its previous field location */
12614       if (!player->is_pushing)
12615         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12616
12617       if (!player->active)
12618         RemovePlayer(player);
12619     }
12620
12621     if (!local_player->LevelSolved && level.use_step_counter)
12622     {
12623       int i;
12624
12625       TimePlayed++;
12626
12627       if (TimeLeft > 0)
12628       {
12629         TimeLeft--;
12630
12631         if (TimeLeft <= 10 && setup.time_limit)
12632           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12633
12634 #if 1
12635         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12636
12637         DisplayGameControlValues();
12638 #else
12639         DrawGameValue_Time(TimeLeft);
12640 #endif
12641
12642         if (!TimeLeft && setup.time_limit)
12643           for (i = 0; i < MAX_PLAYERS; i++)
12644             KillPlayer(&stored_player[i]);
12645       }
12646 #if 1
12647       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12648       {
12649         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12650
12651         DisplayGameControlValues();
12652       }
12653 #else
12654       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12655         DrawGameValue_Time(TimePlayed);
12656 #endif
12657     }
12658
12659     if (tape.single_step && tape.recording && !tape.pausing &&
12660         !player->programmed_action)
12661       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12662   }
12663 }
12664
12665 void ScrollScreen(struct PlayerInfo *player, int mode)
12666 {
12667   static unsigned long screen_frame_counter = 0;
12668
12669   if (mode == SCROLL_INIT)
12670   {
12671     /* set scrolling step size according to actual player's moving speed */
12672     ScrollStepSize = TILEX / player->move_delay_value;
12673
12674     screen_frame_counter = FrameCounter;
12675     ScreenMovDir = player->MovDir;
12676     ScreenMovPos = player->MovPos;
12677     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12678     return;
12679   }
12680   else if (!FrameReached(&screen_frame_counter, 1))
12681     return;
12682
12683   if (ScreenMovPos)
12684   {
12685     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12686     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12687     redraw_mask |= REDRAW_FIELD;
12688   }
12689   else
12690     ScreenMovDir = MV_NONE;
12691 }
12692
12693 void TestIfPlayerTouchesCustomElement(int x, int y)
12694 {
12695   static int xy[4][2] =
12696   {
12697     { 0, -1 },
12698     { -1, 0 },
12699     { +1, 0 },
12700     { 0, +1 }
12701   };
12702   static int trigger_sides[4][2] =
12703   {
12704     /* center side       border side */
12705     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12706     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12707     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12708     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12709   };
12710   static int touch_dir[4] =
12711   {
12712     MV_LEFT | MV_RIGHT,
12713     MV_UP   | MV_DOWN,
12714     MV_UP   | MV_DOWN,
12715     MV_LEFT | MV_RIGHT
12716   };
12717   int center_element = Feld[x][y];      /* should always be non-moving! */
12718   int i;
12719
12720   for (i = 0; i < NUM_DIRECTIONS; i++)
12721   {
12722     int xx = x + xy[i][0];
12723     int yy = y + xy[i][1];
12724     int center_side = trigger_sides[i][0];
12725     int border_side = trigger_sides[i][1];
12726     int border_element;
12727
12728     if (!IN_LEV_FIELD(xx, yy))
12729       continue;
12730
12731     if (IS_PLAYER(x, y))
12732     {
12733       struct PlayerInfo *player = PLAYERINFO(x, y);
12734
12735       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12736         border_element = Feld[xx][yy];          /* may be moving! */
12737       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12738         border_element = Feld[xx][yy];
12739       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12740         border_element = MovingOrBlocked2Element(xx, yy);
12741       else
12742         continue;               /* center and border element do not touch */
12743
12744       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12745                                  player->index_bit, border_side);
12746       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12747                                           CE_PLAYER_TOUCHES_X,
12748                                           player->index_bit, border_side);
12749     }
12750     else if (IS_PLAYER(xx, yy))
12751     {
12752       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12753
12754       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12755       {
12756         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12757           continue;             /* center and border element do not touch */
12758       }
12759
12760       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12761                                  player->index_bit, center_side);
12762       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12763                                           CE_PLAYER_TOUCHES_X,
12764                                           player->index_bit, center_side);
12765       break;
12766     }
12767   }
12768 }
12769
12770 #if USE_ELEMENT_TOUCHING_BUGFIX
12771
12772 void TestIfElementTouchesCustomElement(int x, int y)
12773 {
12774   static int xy[4][2] =
12775   {
12776     { 0, -1 },
12777     { -1, 0 },
12778     { +1, 0 },
12779     { 0, +1 }
12780   };
12781   static int trigger_sides[4][2] =
12782   {
12783     /* center side      border side */
12784     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12785     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12786     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12787     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12788   };
12789   static int touch_dir[4] =
12790   {
12791     MV_LEFT | MV_RIGHT,
12792     MV_UP   | MV_DOWN,
12793     MV_UP   | MV_DOWN,
12794     MV_LEFT | MV_RIGHT
12795   };
12796   boolean change_center_element = FALSE;
12797   int center_element = Feld[x][y];      /* should always be non-moving! */
12798   int border_element_old[NUM_DIRECTIONS];
12799   int i;
12800
12801   for (i = 0; i < NUM_DIRECTIONS; i++)
12802   {
12803     int xx = x + xy[i][0];
12804     int yy = y + xy[i][1];
12805     int border_element;
12806
12807     border_element_old[i] = -1;
12808
12809     if (!IN_LEV_FIELD(xx, yy))
12810       continue;
12811
12812     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12813       border_element = Feld[xx][yy];    /* may be moving! */
12814     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12815       border_element = Feld[xx][yy];
12816     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12817       border_element = MovingOrBlocked2Element(xx, yy);
12818     else
12819       continue;                 /* center and border element do not touch */
12820
12821     border_element_old[i] = border_element;
12822   }
12823
12824   for (i = 0; i < NUM_DIRECTIONS; i++)
12825   {
12826     int xx = x + xy[i][0];
12827     int yy = y + xy[i][1];
12828     int center_side = trigger_sides[i][0];
12829     int border_element = border_element_old[i];
12830
12831     if (border_element == -1)
12832       continue;
12833
12834     /* check for change of border element */
12835     CheckElementChangeBySide(xx, yy, border_element, center_element,
12836                              CE_TOUCHING_X, center_side);
12837   }
12838
12839   for (i = 0; i < NUM_DIRECTIONS; i++)
12840   {
12841     int border_side = trigger_sides[i][1];
12842     int border_element = border_element_old[i];
12843
12844     if (border_element == -1)
12845       continue;
12846
12847     /* check for change of center element (but change it only once) */
12848     if (!change_center_element)
12849       change_center_element =
12850         CheckElementChangeBySide(x, y, center_element, border_element,
12851                                  CE_TOUCHING_X, border_side);
12852   }
12853 }
12854
12855 #else
12856
12857 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12858 {
12859   static int xy[4][2] =
12860   {
12861     { 0, -1 },
12862     { -1, 0 },
12863     { +1, 0 },
12864     { 0, +1 }
12865   };
12866   static int trigger_sides[4][2] =
12867   {
12868     /* center side      border side */
12869     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12870     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12871     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12872     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12873   };
12874   static int touch_dir[4] =
12875   {
12876     MV_LEFT | MV_RIGHT,
12877     MV_UP   | MV_DOWN,
12878     MV_UP   | MV_DOWN,
12879     MV_LEFT | MV_RIGHT
12880   };
12881   boolean change_center_element = FALSE;
12882   int center_element = Feld[x][y];      /* should always be non-moving! */
12883   int i;
12884
12885   for (i = 0; i < NUM_DIRECTIONS; i++)
12886   {
12887     int xx = x + xy[i][0];
12888     int yy = y + xy[i][1];
12889     int center_side = trigger_sides[i][0];
12890     int border_side = trigger_sides[i][1];
12891     int border_element;
12892
12893     if (!IN_LEV_FIELD(xx, yy))
12894       continue;
12895
12896     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12897       border_element = Feld[xx][yy];    /* may be moving! */
12898     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12899       border_element = Feld[xx][yy];
12900     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12901       border_element = MovingOrBlocked2Element(xx, yy);
12902     else
12903       continue;                 /* center and border element do not touch */
12904
12905     /* check for change of center element (but change it only once) */
12906     if (!change_center_element)
12907       change_center_element =
12908         CheckElementChangeBySide(x, y, center_element, border_element,
12909                                  CE_TOUCHING_X, border_side);
12910
12911     /* check for change of border element */
12912     CheckElementChangeBySide(xx, yy, border_element, center_element,
12913                              CE_TOUCHING_X, center_side);
12914   }
12915 }
12916
12917 #endif
12918
12919 void TestIfElementHitsCustomElement(int x, int y, int direction)
12920 {
12921   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12922   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12923   int hitx = x + dx, hity = y + dy;
12924   int hitting_element = Feld[x][y];
12925   int touched_element;
12926
12927   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12928     return;
12929
12930   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12931                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12932
12933   if (IN_LEV_FIELD(hitx, hity))
12934   {
12935     int opposite_direction = MV_DIR_OPPOSITE(direction);
12936     int hitting_side = direction;
12937     int touched_side = opposite_direction;
12938     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12939                           MovDir[hitx][hity] != direction ||
12940                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12941
12942     object_hit = TRUE;
12943
12944     if (object_hit)
12945     {
12946       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12947                                CE_HITTING_X, touched_side);
12948
12949       CheckElementChangeBySide(hitx, hity, touched_element,
12950                                hitting_element, CE_HIT_BY_X, hitting_side);
12951
12952       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12953                                CE_HIT_BY_SOMETHING, opposite_direction);
12954     }
12955   }
12956
12957   /* "hitting something" is also true when hitting the playfield border */
12958   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12959                            CE_HITTING_SOMETHING, direction);
12960 }
12961
12962 #if 0
12963 void TestIfElementSmashesCustomElement(int x, int y, int direction)
12964 {
12965   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12966   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12967   int hitx = x + dx, hity = y + dy;
12968   int hitting_element = Feld[x][y];
12969   int touched_element;
12970 #if 0
12971   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
12972                         !IS_FREE(hitx, hity) &&
12973                         (!IS_MOVING(hitx, hity) ||
12974                          MovDir[hitx][hity] != direction ||
12975                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
12976 #endif
12977
12978   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12979     return;
12980
12981 #if 0
12982   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
12983     return;
12984 #endif
12985
12986   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12987                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12988
12989   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12990                            EP_CAN_SMASH_EVERYTHING, direction);
12991
12992   if (IN_LEV_FIELD(hitx, hity))
12993   {
12994     int opposite_direction = MV_DIR_OPPOSITE(direction);
12995     int hitting_side = direction;
12996     int touched_side = opposite_direction;
12997 #if 0
12998     int touched_element = MovingOrBlocked2Element(hitx, hity);
12999 #endif
13000 #if 1
13001     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13002                           MovDir[hitx][hity] != direction ||
13003                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13004
13005     object_hit = TRUE;
13006 #endif
13007
13008     if (object_hit)
13009     {
13010       int i;
13011
13012       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13013                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13014
13015       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13016                                CE_OTHER_IS_SMASHING, touched_side);
13017
13018       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13019                                CE_OTHER_GETS_SMASHED, hitting_side);
13020     }
13021   }
13022 }
13023 #endif
13024
13025 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13026 {
13027   int i, kill_x = -1, kill_y = -1;
13028
13029   int bad_element = -1;
13030   static int test_xy[4][2] =
13031   {
13032     { 0, -1 },
13033     { -1, 0 },
13034     { +1, 0 },
13035     { 0, +1 }
13036   };
13037   static int test_dir[4] =
13038   {
13039     MV_UP,
13040     MV_LEFT,
13041     MV_RIGHT,
13042     MV_DOWN
13043   };
13044
13045   for (i = 0; i < NUM_DIRECTIONS; i++)
13046   {
13047     int test_x, test_y, test_move_dir, test_element;
13048
13049     test_x = good_x + test_xy[i][0];
13050     test_y = good_y + test_xy[i][1];
13051
13052     if (!IN_LEV_FIELD(test_x, test_y))
13053       continue;
13054
13055     test_move_dir =
13056       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13057
13058     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13059
13060     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13061        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13062     */
13063     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13064         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13065     {
13066       kill_x = test_x;
13067       kill_y = test_y;
13068       bad_element = test_element;
13069
13070       break;
13071     }
13072   }
13073
13074   if (kill_x != -1 || kill_y != -1)
13075   {
13076     if (IS_PLAYER(good_x, good_y))
13077     {
13078       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13079
13080       if (player->shield_deadly_time_left > 0 &&
13081           !IS_INDESTRUCTIBLE(bad_element))
13082         Bang(kill_x, kill_y);
13083       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13084         KillPlayer(player);
13085     }
13086     else
13087       Bang(good_x, good_y);
13088   }
13089 }
13090
13091 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13092 {
13093   int i, kill_x = -1, kill_y = -1;
13094   int bad_element = Feld[bad_x][bad_y];
13095   static int test_xy[4][2] =
13096   {
13097     { 0, -1 },
13098     { -1, 0 },
13099     { +1, 0 },
13100     { 0, +1 }
13101   };
13102   static int touch_dir[4] =
13103   {
13104     MV_LEFT | MV_RIGHT,
13105     MV_UP   | MV_DOWN,
13106     MV_UP   | MV_DOWN,
13107     MV_LEFT | MV_RIGHT
13108   };
13109   static int test_dir[4] =
13110   {
13111     MV_UP,
13112     MV_LEFT,
13113     MV_RIGHT,
13114     MV_DOWN
13115   };
13116
13117   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13118     return;
13119
13120   for (i = 0; i < NUM_DIRECTIONS; i++)
13121   {
13122     int test_x, test_y, test_move_dir, test_element;
13123
13124     test_x = bad_x + test_xy[i][0];
13125     test_y = bad_y + test_xy[i][1];
13126     if (!IN_LEV_FIELD(test_x, test_y))
13127       continue;
13128
13129     test_move_dir =
13130       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13131
13132     test_element = Feld[test_x][test_y];
13133
13134     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13135        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13136     */
13137     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13138         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13139     {
13140       /* good thing is player or penguin that does not move away */
13141       if (IS_PLAYER(test_x, test_y))
13142       {
13143         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13144
13145         if (bad_element == EL_ROBOT && player->is_moving)
13146           continue;     /* robot does not kill player if he is moving */
13147
13148         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13149         {
13150           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13151             continue;           /* center and border element do not touch */
13152         }
13153
13154         kill_x = test_x;
13155         kill_y = test_y;
13156         break;
13157       }
13158       else if (test_element == EL_PENGUIN)
13159       {
13160         kill_x = test_x;
13161         kill_y = test_y;
13162         break;
13163       }
13164     }
13165   }
13166
13167   if (kill_x != -1 || kill_y != -1)
13168   {
13169     if (IS_PLAYER(kill_x, kill_y))
13170     {
13171       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13172
13173       if (player->shield_deadly_time_left > 0 &&
13174           !IS_INDESTRUCTIBLE(bad_element))
13175         Bang(bad_x, bad_y);
13176       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13177         KillPlayer(player);
13178     }
13179     else
13180       Bang(kill_x, kill_y);
13181   }
13182 }
13183
13184 void TestIfPlayerTouchesBadThing(int x, int y)
13185 {
13186   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13187 }
13188
13189 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13190 {
13191   TestIfGoodThingHitsBadThing(x, y, move_dir);
13192 }
13193
13194 void TestIfBadThingTouchesPlayer(int x, int y)
13195 {
13196   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13197 }
13198
13199 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13200 {
13201   TestIfBadThingHitsGoodThing(x, y, move_dir);
13202 }
13203
13204 void TestIfFriendTouchesBadThing(int x, int y)
13205 {
13206   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13207 }
13208
13209 void TestIfBadThingTouchesFriend(int x, int y)
13210 {
13211   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13212 }
13213
13214 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13215 {
13216   int i, kill_x = bad_x, kill_y = bad_y;
13217   static int xy[4][2] =
13218   {
13219     { 0, -1 },
13220     { -1, 0 },
13221     { +1, 0 },
13222     { 0, +1 }
13223   };
13224
13225   for (i = 0; i < NUM_DIRECTIONS; i++)
13226   {
13227     int x, y, element;
13228
13229     x = bad_x + xy[i][0];
13230     y = bad_y + xy[i][1];
13231     if (!IN_LEV_FIELD(x, y))
13232       continue;
13233
13234     element = Feld[x][y];
13235     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13236         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13237     {
13238       kill_x = x;
13239       kill_y = y;
13240       break;
13241     }
13242   }
13243
13244   if (kill_x != bad_x || kill_y != bad_y)
13245     Bang(bad_x, bad_y);
13246 }
13247
13248 void KillPlayer(struct PlayerInfo *player)
13249 {
13250   int jx = player->jx, jy = player->jy;
13251
13252   if (!player->active)
13253     return;
13254
13255   /* the following code was introduced to prevent an infinite loop when calling
13256      -> Bang()
13257      -> CheckTriggeredElementChangeExt()
13258      -> ExecuteCustomElementAction()
13259      -> KillPlayer()
13260      -> (infinitely repeating the above sequence of function calls)
13261      which occurs when killing the player while having a CE with the setting
13262      "kill player X when explosion of <player X>"; the solution using a new
13263      field "player->killed" was chosen for backwards compatibility, although
13264      clever use of the fields "player->active" etc. would probably also work */
13265 #if 1
13266   if (player->killed)
13267     return;
13268 #endif
13269
13270   player->killed = TRUE;
13271
13272   /* remove accessible field at the player's position */
13273   Feld[jx][jy] = EL_EMPTY;
13274
13275   /* deactivate shield (else Bang()/Explode() would not work right) */
13276   player->shield_normal_time_left = 0;
13277   player->shield_deadly_time_left = 0;
13278
13279   Bang(jx, jy);
13280   BuryPlayer(player);
13281 }
13282
13283 static void KillPlayerUnlessEnemyProtected(int x, int y)
13284 {
13285   if (!PLAYER_ENEMY_PROTECTED(x, y))
13286     KillPlayer(PLAYERINFO(x, y));
13287 }
13288
13289 static void KillPlayerUnlessExplosionProtected(int x, int y)
13290 {
13291   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13292     KillPlayer(PLAYERINFO(x, y));
13293 }
13294
13295 void BuryPlayer(struct PlayerInfo *player)
13296 {
13297   int jx = player->jx, jy = player->jy;
13298
13299   if (!player->active)
13300     return;
13301
13302   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13303   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13304
13305   player->GameOver = TRUE;
13306   RemovePlayer(player);
13307 }
13308
13309 void RemovePlayer(struct PlayerInfo *player)
13310 {
13311   int jx = player->jx, jy = player->jy;
13312   int i, found = FALSE;
13313
13314   player->present = FALSE;
13315   player->active = FALSE;
13316
13317   if (!ExplodeField[jx][jy])
13318     StorePlayer[jx][jy] = 0;
13319
13320   if (player->is_moving)
13321     DrawLevelField(player->last_jx, player->last_jy);
13322
13323   for (i = 0; i < MAX_PLAYERS; i++)
13324     if (stored_player[i].active)
13325       found = TRUE;
13326
13327   if (!found)
13328     AllPlayersGone = TRUE;
13329
13330   ExitX = ZX = jx;
13331   ExitY = ZY = jy;
13332 }
13333
13334 #if USE_NEW_SNAP_DELAY
13335 static void setFieldForSnapping(int x, int y, int element, int direction)
13336 {
13337   struct ElementInfo *ei = &element_info[element];
13338   int direction_bit = MV_DIR_TO_BIT(direction);
13339   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13340   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13341                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13342
13343   Feld[x][y] = EL_ELEMENT_SNAPPING;
13344   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13345
13346   ResetGfxAnimation(x, y);
13347
13348   GfxElement[x][y] = element;
13349   GfxAction[x][y] = action;
13350   GfxDir[x][y] = direction;
13351   GfxFrame[x][y] = -1;
13352 }
13353 #endif
13354
13355 /*
13356   =============================================================================
13357   checkDiagonalPushing()
13358   -----------------------------------------------------------------------------
13359   check if diagonal input device direction results in pushing of object
13360   (by checking if the alternative direction is walkable, diggable, ...)
13361   =============================================================================
13362 */
13363
13364 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13365                                     int x, int y, int real_dx, int real_dy)
13366 {
13367   int jx, jy, dx, dy, xx, yy;
13368
13369   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13370     return TRUE;
13371
13372   /* diagonal direction: check alternative direction */
13373   jx = player->jx;
13374   jy = player->jy;
13375   dx = x - jx;
13376   dy = y - jy;
13377   xx = jx + (dx == 0 ? real_dx : 0);
13378   yy = jy + (dy == 0 ? real_dy : 0);
13379
13380   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13381 }
13382
13383 /*
13384   =============================================================================
13385   DigField()
13386   -----------------------------------------------------------------------------
13387   x, y:                 field next to player (non-diagonal) to try to dig to
13388   real_dx, real_dy:     direction as read from input device (can be diagonal)
13389   =============================================================================
13390 */
13391
13392 int DigField(struct PlayerInfo *player,
13393              int oldx, int oldy, int x, int y,
13394              int real_dx, int real_dy, int mode)
13395 {
13396   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13397   boolean player_was_pushing = player->is_pushing;
13398   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13399   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13400   int jx = oldx, jy = oldy;
13401   int dx = x - jx, dy = y - jy;
13402   int nextx = x + dx, nexty = y + dy;
13403   int move_direction = (dx == -1 ? MV_LEFT  :
13404                         dx == +1 ? MV_RIGHT :
13405                         dy == -1 ? MV_UP    :
13406                         dy == +1 ? MV_DOWN  : MV_NONE);
13407   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13408   int dig_side = MV_DIR_OPPOSITE(move_direction);
13409   int old_element = Feld[jx][jy];
13410 #if USE_FIXED_DONT_RUN_INTO
13411   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13412 #else
13413   int element;
13414 #endif
13415   int collect_count;
13416
13417   if (is_player)                /* function can also be called by EL_PENGUIN */
13418   {
13419     if (player->MovPos == 0)
13420     {
13421       player->is_digging = FALSE;
13422       player->is_collecting = FALSE;
13423     }
13424
13425     if (player->MovPos == 0)    /* last pushing move finished */
13426       player->is_pushing = FALSE;
13427
13428     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13429     {
13430       player->is_switching = FALSE;
13431       player->push_delay = -1;
13432
13433       return MP_NO_ACTION;
13434     }
13435   }
13436
13437 #if !USE_FIXED_DONT_RUN_INTO
13438   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13439     return MP_NO_ACTION;
13440 #endif
13441
13442   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13443     old_element = Back[jx][jy];
13444
13445   /* in case of element dropped at player position, check background */
13446   else if (Back[jx][jy] != EL_EMPTY &&
13447            game.engine_version >= VERSION_IDENT(2,2,0,0))
13448     old_element = Back[jx][jy];
13449
13450   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13451     return MP_NO_ACTION;        /* field has no opening in this direction */
13452
13453   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13454     return MP_NO_ACTION;        /* field has no opening in this direction */
13455
13456 #if USE_FIXED_DONT_RUN_INTO
13457   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13458   {
13459     SplashAcid(x, y);
13460
13461     Feld[jx][jy] = player->artwork_element;
13462     InitMovingField(jx, jy, MV_DOWN);
13463     Store[jx][jy] = EL_ACID;
13464     ContinueMoving(jx, jy);
13465     BuryPlayer(player);
13466
13467     return MP_DONT_RUN_INTO;
13468   }
13469 #endif
13470
13471 #if USE_FIXED_DONT_RUN_INTO
13472   if (player_can_move && DONT_RUN_INTO(element))
13473   {
13474     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13475
13476     return MP_DONT_RUN_INTO;
13477   }
13478 #endif
13479
13480 #if USE_FIXED_DONT_RUN_INTO
13481   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13482     return MP_NO_ACTION;
13483 #endif
13484
13485 #if !USE_FIXED_DONT_RUN_INTO
13486   element = Feld[x][y];
13487 #endif
13488
13489   collect_count = element_info[element].collect_count_initial;
13490
13491   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13492     return MP_NO_ACTION;
13493
13494   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13495     player_can_move = player_can_move_or_snap;
13496
13497   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13498       game.engine_version >= VERSION_IDENT(2,2,0,0))
13499   {
13500     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13501                                player->index_bit, dig_side);
13502     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13503                                         player->index_bit, dig_side);
13504
13505     if (element == EL_DC_LANDMINE)
13506       Bang(x, y);
13507
13508     if (Feld[x][y] != element)          /* field changed by snapping */
13509       return MP_ACTION;
13510
13511     return MP_NO_ACTION;
13512   }
13513
13514 #if USE_PLAYER_GRAVITY
13515   if (player->gravity && is_player && !player->is_auto_moving &&
13516       canFallDown(player) && move_direction != MV_DOWN &&
13517       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13518     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13519 #else
13520   if (game.gravity && is_player && !player->is_auto_moving &&
13521       canFallDown(player) && move_direction != MV_DOWN &&
13522       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13523     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13524 #endif
13525
13526   if (player_can_move &&
13527       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13528   {
13529     int sound_element = SND_ELEMENT(element);
13530     int sound_action = ACTION_WALKING;
13531
13532     if (IS_RND_GATE(element))
13533     {
13534       if (!player->key[RND_GATE_NR(element)])
13535         return MP_NO_ACTION;
13536     }
13537     else if (IS_RND_GATE_GRAY(element))
13538     {
13539       if (!player->key[RND_GATE_GRAY_NR(element)])
13540         return MP_NO_ACTION;
13541     }
13542     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13543     {
13544       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13545         return MP_NO_ACTION;
13546     }
13547     else if (element == EL_EXIT_OPEN ||
13548              element == EL_EM_EXIT_OPEN ||
13549              element == EL_STEEL_EXIT_OPEN ||
13550              element == EL_EM_STEEL_EXIT_OPEN ||
13551              element == EL_SP_EXIT_OPEN ||
13552              element == EL_SP_EXIT_OPENING)
13553     {
13554       sound_action = ACTION_PASSING;    /* player is passing exit */
13555     }
13556     else if (element == EL_EMPTY)
13557     {
13558       sound_action = ACTION_MOVING;             /* nothing to walk on */
13559     }
13560
13561     /* play sound from background or player, whatever is available */
13562     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13563       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13564     else
13565       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13566   }
13567   else if (player_can_move &&
13568            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13569   {
13570     if (!ACCESS_FROM(element, opposite_direction))
13571       return MP_NO_ACTION;      /* field not accessible from this direction */
13572
13573     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13574       return MP_NO_ACTION;
13575
13576     if (IS_EM_GATE(element))
13577     {
13578       if (!player->key[EM_GATE_NR(element)])
13579         return MP_NO_ACTION;
13580     }
13581     else if (IS_EM_GATE_GRAY(element))
13582     {
13583       if (!player->key[EM_GATE_GRAY_NR(element)])
13584         return MP_NO_ACTION;
13585     }
13586     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13587     {
13588       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13589         return MP_NO_ACTION;
13590     }
13591     else if (IS_EMC_GATE(element))
13592     {
13593       if (!player->key[EMC_GATE_NR(element)])
13594         return MP_NO_ACTION;
13595     }
13596     else if (IS_EMC_GATE_GRAY(element))
13597     {
13598       if (!player->key[EMC_GATE_GRAY_NR(element)])
13599         return MP_NO_ACTION;
13600     }
13601     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13602     {
13603       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13604         return MP_NO_ACTION;
13605     }
13606     else if (element == EL_DC_GATE_WHITE ||
13607              element == EL_DC_GATE_WHITE_GRAY ||
13608              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13609     {
13610       if (player->num_white_keys == 0)
13611         return MP_NO_ACTION;
13612
13613       player->num_white_keys--;
13614     }
13615     else if (IS_SP_PORT(element))
13616     {
13617       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13618           element == EL_SP_GRAVITY_PORT_RIGHT ||
13619           element == EL_SP_GRAVITY_PORT_UP ||
13620           element == EL_SP_GRAVITY_PORT_DOWN)
13621 #if USE_PLAYER_GRAVITY
13622         player->gravity = !player->gravity;
13623 #else
13624         game.gravity = !game.gravity;
13625 #endif
13626       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13627                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13628                element == EL_SP_GRAVITY_ON_PORT_UP ||
13629                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13630 #if USE_PLAYER_GRAVITY
13631         player->gravity = TRUE;
13632 #else
13633         game.gravity = TRUE;
13634 #endif
13635       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13636                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13637                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13638                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13639 #if USE_PLAYER_GRAVITY
13640         player->gravity = FALSE;
13641 #else
13642         game.gravity = FALSE;
13643 #endif
13644     }
13645
13646     /* automatically move to the next field with double speed */
13647     player->programmed_action = move_direction;
13648
13649     if (player->move_delay_reset_counter == 0)
13650     {
13651       player->move_delay_reset_counter = 2;     /* two double speed steps */
13652
13653       DOUBLE_PLAYER_SPEED(player);
13654     }
13655
13656     PlayLevelSoundAction(x, y, ACTION_PASSING);
13657   }
13658   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13659   {
13660     RemoveField(x, y);
13661
13662     if (mode != DF_SNAP)
13663     {
13664       GfxElement[x][y] = GFX_ELEMENT(element);
13665       player->is_digging = TRUE;
13666     }
13667
13668     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13669
13670     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13671                                         player->index_bit, dig_side);
13672
13673     if (mode == DF_SNAP)
13674     {
13675 #if USE_NEW_SNAP_DELAY
13676       if (level.block_snap_field)
13677         setFieldForSnapping(x, y, element, move_direction);
13678       else
13679         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13680 #else
13681       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13682 #endif
13683
13684       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13685                                           player->index_bit, dig_side);
13686     }
13687   }
13688   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13689   {
13690     RemoveField(x, y);
13691
13692     if (is_player && mode != DF_SNAP)
13693     {
13694       GfxElement[x][y] = element;
13695       player->is_collecting = TRUE;
13696     }
13697
13698     if (element == EL_SPEED_PILL)
13699     {
13700       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13701     }
13702     else if (element == EL_EXTRA_TIME && level.time > 0)
13703     {
13704       TimeLeft += level.extra_time;
13705
13706 #if 1
13707       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13708
13709       DisplayGameControlValues();
13710 #else
13711       DrawGameValue_Time(TimeLeft);
13712 #endif
13713     }
13714     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13715     {
13716       player->shield_normal_time_left += level.shield_normal_time;
13717       if (element == EL_SHIELD_DEADLY)
13718         player->shield_deadly_time_left += level.shield_deadly_time;
13719     }
13720     else if (element == EL_DYNAMITE ||
13721              element == EL_EM_DYNAMITE ||
13722              element == EL_SP_DISK_RED)
13723     {
13724       if (player->inventory_size < MAX_INVENTORY_SIZE)
13725         player->inventory_element[player->inventory_size++] = element;
13726
13727       DrawGameDoorValues();
13728     }
13729     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13730     {
13731       player->dynabomb_count++;
13732       player->dynabombs_left++;
13733     }
13734     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13735     {
13736       player->dynabomb_size++;
13737     }
13738     else if (element == EL_DYNABOMB_INCREASE_POWER)
13739     {
13740       player->dynabomb_xl = TRUE;
13741     }
13742     else if (IS_KEY(element))
13743     {
13744       player->key[KEY_NR(element)] = TRUE;
13745
13746       DrawGameDoorValues();
13747     }
13748     else if (element == EL_DC_KEY_WHITE)
13749     {
13750       player->num_white_keys++;
13751
13752       /* display white keys? */
13753       /* DrawGameDoorValues(); */
13754     }
13755     else if (IS_ENVELOPE(element))
13756     {
13757       player->show_envelope = element;
13758     }
13759     else if (element == EL_EMC_LENSES)
13760     {
13761       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13762
13763       RedrawAllInvisibleElementsForLenses();
13764     }
13765     else if (element == EL_EMC_MAGNIFIER)
13766     {
13767       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13768
13769       RedrawAllInvisibleElementsForMagnifier();
13770     }
13771     else if (IS_DROPPABLE(element) ||
13772              IS_THROWABLE(element))     /* can be collected and dropped */
13773     {
13774       int i;
13775
13776       if (collect_count == 0)
13777         player->inventory_infinite_element = element;
13778       else
13779         for (i = 0; i < collect_count; i++)
13780           if (player->inventory_size < MAX_INVENTORY_SIZE)
13781             player->inventory_element[player->inventory_size++] = element;
13782
13783       DrawGameDoorValues();
13784     }
13785     else if (collect_count > 0)
13786     {
13787       local_player->gems_still_needed -= collect_count;
13788       if (local_player->gems_still_needed < 0)
13789         local_player->gems_still_needed = 0;
13790
13791 #if 1
13792       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13793
13794       DisplayGameControlValues();
13795 #else
13796       DrawGameValue_Emeralds(local_player->gems_still_needed);
13797 #endif
13798     }
13799
13800     RaiseScoreElement(element);
13801     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13802
13803     if (is_player)
13804       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13805                                           player->index_bit, dig_side);
13806
13807     if (mode == DF_SNAP)
13808     {
13809 #if USE_NEW_SNAP_DELAY
13810       if (level.block_snap_field)
13811         setFieldForSnapping(x, y, element, move_direction);
13812       else
13813         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13814 #else
13815       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13816 #endif
13817
13818       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13819                                           player->index_bit, dig_side);
13820     }
13821   }
13822   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13823   {
13824     if (mode == DF_SNAP && element != EL_BD_ROCK)
13825       return MP_NO_ACTION;
13826
13827     if (CAN_FALL(element) && dy)
13828       return MP_NO_ACTION;
13829
13830     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13831         !(element == EL_SPRING && level.use_spring_bug))
13832       return MP_NO_ACTION;
13833
13834     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13835         ((move_direction & MV_VERTICAL &&
13836           ((element_info[element].move_pattern & MV_LEFT &&
13837             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13838            (element_info[element].move_pattern & MV_RIGHT &&
13839             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13840          (move_direction & MV_HORIZONTAL &&
13841           ((element_info[element].move_pattern & MV_UP &&
13842             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13843            (element_info[element].move_pattern & MV_DOWN &&
13844             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13845       return MP_NO_ACTION;
13846
13847     /* do not push elements already moving away faster than player */
13848     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13849         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13850       return MP_NO_ACTION;
13851
13852     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13853     {
13854       if (player->push_delay_value == -1 || !player_was_pushing)
13855         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13856     }
13857     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13858     {
13859       if (player->push_delay_value == -1)
13860         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13861     }
13862     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13863     {
13864       if (!player->is_pushing)
13865         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13866     }
13867
13868     player->is_pushing = TRUE;
13869     player->is_active = TRUE;
13870
13871     if (!(IN_LEV_FIELD(nextx, nexty) &&
13872           (IS_FREE(nextx, nexty) ||
13873            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
13874             IS_SB_ELEMENT(element)))))
13875       return MP_NO_ACTION;
13876
13877     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13878       return MP_NO_ACTION;
13879
13880     if (player->push_delay == -1)       /* new pushing; restart delay */
13881       player->push_delay = 0;
13882
13883     if (player->push_delay < player->push_delay_value &&
13884         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13885         element != EL_SPRING && element != EL_BALLOON)
13886     {
13887       /* make sure that there is no move delay before next try to push */
13888       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13889         player->move_delay = 0;
13890
13891       return MP_NO_ACTION;
13892     }
13893
13894     if (IS_SB_ELEMENT(element))
13895     {
13896       if (element == EL_SOKOBAN_FIELD_FULL)
13897       {
13898         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13899         local_player->sokobanfields_still_needed++;
13900       }
13901
13902       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13903       {
13904         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13905         local_player->sokobanfields_still_needed--;
13906       }
13907
13908       Feld[x][y] = EL_SOKOBAN_OBJECT;
13909
13910       if (Back[x][y] == Back[nextx][nexty])
13911         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13912       else if (Back[x][y] != 0)
13913         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13914                                     ACTION_EMPTYING);
13915       else
13916         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13917                                     ACTION_FILLING);
13918
13919       if (local_player->sokobanfields_still_needed == 0 &&
13920           game.emulation == EMU_SOKOBAN)
13921       {
13922         PlayerWins(player);
13923
13924         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13925       }
13926     }
13927     else
13928       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13929
13930     InitMovingField(x, y, move_direction);
13931     GfxAction[x][y] = ACTION_PUSHING;
13932
13933     if (mode == DF_SNAP)
13934       ContinueMoving(x, y);
13935     else
13936       MovPos[x][y] = (dx != 0 ? dx : dy);
13937
13938     Pushed[x][y] = TRUE;
13939     Pushed[nextx][nexty] = TRUE;
13940
13941     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13942       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13943     else
13944       player->push_delay_value = -1;    /* get new value later */
13945
13946     /* check for element change _after_ element has been pushed */
13947     if (game.use_change_when_pushing_bug)
13948     {
13949       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13950                                  player->index_bit, dig_side);
13951       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13952                                           player->index_bit, dig_side);
13953     }
13954   }
13955   else if (IS_SWITCHABLE(element))
13956   {
13957     if (PLAYER_SWITCHING(player, x, y))
13958     {
13959       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13960                                           player->index_bit, dig_side);
13961
13962       return MP_ACTION;
13963     }
13964
13965     player->is_switching = TRUE;
13966     player->switch_x = x;
13967     player->switch_y = y;
13968
13969     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13970
13971     if (element == EL_ROBOT_WHEEL)
13972     {
13973       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13974       ZX = x;
13975       ZY = y;
13976
13977       game.robot_wheel_active = TRUE;
13978
13979       DrawLevelField(x, y);
13980     }
13981     else if (element == EL_SP_TERMINAL)
13982     {
13983       int xx, yy;
13984
13985       SCAN_PLAYFIELD(xx, yy)
13986       {
13987         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13988           Bang(xx, yy);
13989         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13990           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13991       }
13992     }
13993     else if (IS_BELT_SWITCH(element))
13994     {
13995       ToggleBeltSwitch(x, y);
13996     }
13997     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13998              element == EL_SWITCHGATE_SWITCH_DOWN ||
13999              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14000              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14001     {
14002       ToggleSwitchgateSwitch(x, y);
14003     }
14004     else if (element == EL_LIGHT_SWITCH ||
14005              element == EL_LIGHT_SWITCH_ACTIVE)
14006     {
14007       ToggleLightSwitch(x, y);
14008     }
14009     else if (element == EL_TIMEGATE_SWITCH ||
14010              element == EL_DC_TIMEGATE_SWITCH)
14011     {
14012       ActivateTimegateSwitch(x, y);
14013     }
14014     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14015              element == EL_BALLOON_SWITCH_RIGHT ||
14016              element == EL_BALLOON_SWITCH_UP    ||
14017              element == EL_BALLOON_SWITCH_DOWN  ||
14018              element == EL_BALLOON_SWITCH_NONE  ||
14019              element == EL_BALLOON_SWITCH_ANY)
14020     {
14021       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14022                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14023                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14024                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14025                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14026                              move_direction);
14027     }
14028     else if (element == EL_LAMP)
14029     {
14030       Feld[x][y] = EL_LAMP_ACTIVE;
14031       local_player->lights_still_needed--;
14032
14033       ResetGfxAnimation(x, y);
14034       DrawLevelField(x, y);
14035     }
14036     else if (element == EL_TIME_ORB_FULL)
14037     {
14038       Feld[x][y] = EL_TIME_ORB_EMPTY;
14039
14040       if (level.time > 0 || level.use_time_orb_bug)
14041       {
14042         TimeLeft += level.time_orb_time;
14043
14044 #if 1
14045         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14046
14047         DisplayGameControlValues();
14048 #else
14049         DrawGameValue_Time(TimeLeft);
14050 #endif
14051       }
14052
14053       ResetGfxAnimation(x, y);
14054       DrawLevelField(x, y);
14055     }
14056     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14057              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14058     {
14059       int xx, yy;
14060
14061       game.ball_state = !game.ball_state;
14062
14063       SCAN_PLAYFIELD(xx, yy)
14064       {
14065         int e = Feld[xx][yy];
14066
14067         if (game.ball_state)
14068         {
14069           if (e == EL_EMC_MAGIC_BALL)
14070             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14071           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14072             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14073         }
14074         else
14075         {
14076           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14077             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14078           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14079             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14080         }
14081       }
14082     }
14083
14084     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14085                                         player->index_bit, dig_side);
14086
14087     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14088                                         player->index_bit, dig_side);
14089
14090     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14091                                         player->index_bit, dig_side);
14092
14093     return MP_ACTION;
14094   }
14095   else
14096   {
14097     if (!PLAYER_SWITCHING(player, x, y))
14098     {
14099       player->is_switching = TRUE;
14100       player->switch_x = x;
14101       player->switch_y = y;
14102
14103       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14104                                  player->index_bit, dig_side);
14105       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14106                                           player->index_bit, dig_side);
14107
14108       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14109                                  player->index_bit, dig_side);
14110       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14111                                           player->index_bit, dig_side);
14112     }
14113
14114     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14115                                player->index_bit, dig_side);
14116     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14117                                         player->index_bit, dig_side);
14118
14119     return MP_NO_ACTION;
14120   }
14121
14122   player->push_delay = -1;
14123
14124   if (is_player)                /* function can also be called by EL_PENGUIN */
14125   {
14126     if (Feld[x][y] != element)          /* really digged/collected something */
14127     {
14128       player->is_collecting = !player->is_digging;
14129       player->is_active = TRUE;
14130     }
14131   }
14132
14133   return MP_MOVING;
14134 }
14135
14136 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14137 {
14138   int jx = player->jx, jy = player->jy;
14139   int x = jx + dx, y = jy + dy;
14140   int snap_direction = (dx == -1 ? MV_LEFT  :
14141                         dx == +1 ? MV_RIGHT :
14142                         dy == -1 ? MV_UP    :
14143                         dy == +1 ? MV_DOWN  : MV_NONE);
14144   boolean can_continue_snapping = (level.continuous_snapping &&
14145                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14146
14147   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14148     return FALSE;
14149
14150   if (!player->active || !IN_LEV_FIELD(x, y))
14151     return FALSE;
14152
14153   if (dx && dy)
14154     return FALSE;
14155
14156   if (!dx && !dy)
14157   {
14158     if (player->MovPos == 0)
14159       player->is_pushing = FALSE;
14160
14161     player->is_snapping = FALSE;
14162
14163     if (player->MovPos == 0)
14164     {
14165       player->is_moving = FALSE;
14166       player->is_digging = FALSE;
14167       player->is_collecting = FALSE;
14168     }
14169
14170     return FALSE;
14171   }
14172
14173 #if USE_NEW_CONTINUOUS_SNAPPING
14174   /* prevent snapping with already pressed snap key when not allowed */
14175   if (player->is_snapping && !can_continue_snapping)
14176     return FALSE;
14177 #else
14178   if (player->is_snapping)
14179     return FALSE;
14180 #endif
14181
14182   player->MovDir = snap_direction;
14183
14184   if (player->MovPos == 0)
14185   {
14186     player->is_moving = FALSE;
14187     player->is_digging = FALSE;
14188     player->is_collecting = FALSE;
14189   }
14190
14191   player->is_dropping = FALSE;
14192   player->is_dropping_pressed = FALSE;
14193   player->drop_pressed_delay = 0;
14194
14195   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14196     return FALSE;
14197
14198   player->is_snapping = TRUE;
14199   player->is_active = TRUE;
14200
14201   if (player->MovPos == 0)
14202   {
14203     player->is_moving = FALSE;
14204     player->is_digging = FALSE;
14205     player->is_collecting = FALSE;
14206   }
14207
14208   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14209     DrawLevelField(player->last_jx, player->last_jy);
14210
14211   DrawLevelField(x, y);
14212
14213   return TRUE;
14214 }
14215
14216 boolean DropElement(struct PlayerInfo *player)
14217 {
14218   int old_element, new_element;
14219   int dropx = player->jx, dropy = player->jy;
14220   int drop_direction = player->MovDir;
14221   int drop_side = drop_direction;
14222 #if 1
14223   int drop_element = get_next_dropped_element(player);
14224 #else
14225   int drop_element = (player->inventory_size > 0 ?
14226                       player->inventory_element[player->inventory_size - 1] :
14227                       player->inventory_infinite_element != EL_UNDEFINED ?
14228                       player->inventory_infinite_element :
14229                       player->dynabombs_left > 0 ?
14230                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14231                       EL_UNDEFINED);
14232 #endif
14233
14234   player->is_dropping_pressed = TRUE;
14235
14236   /* do not drop an element on top of another element; when holding drop key
14237      pressed without moving, dropped element must move away before the next
14238      element can be dropped (this is especially important if the next element
14239      is dynamite, which can be placed on background for historical reasons) */
14240   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14241     return MP_ACTION;
14242
14243   if (IS_THROWABLE(drop_element))
14244   {
14245     dropx += GET_DX_FROM_DIR(drop_direction);
14246     dropy += GET_DY_FROM_DIR(drop_direction);
14247
14248     if (!IN_LEV_FIELD(dropx, dropy))
14249       return FALSE;
14250   }
14251
14252   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14253   new_element = drop_element;           /* default: no change when dropping */
14254
14255   /* check if player is active, not moving and ready to drop */
14256   if (!player->active || player->MovPos || player->drop_delay > 0)
14257     return FALSE;
14258
14259   /* check if player has anything that can be dropped */
14260   if (new_element == EL_UNDEFINED)
14261     return FALSE;
14262
14263   /* check if drop key was pressed long enough for EM style dynamite */
14264   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14265     return FALSE;
14266
14267   /* check if anything can be dropped at the current position */
14268   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14269     return FALSE;
14270
14271   /* collected custom elements can only be dropped on empty fields */
14272   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14273     return FALSE;
14274
14275   if (old_element != EL_EMPTY)
14276     Back[dropx][dropy] = old_element;   /* store old element on this field */
14277
14278   ResetGfxAnimation(dropx, dropy);
14279   ResetRandomAnimationValue(dropx, dropy);
14280
14281   if (player->inventory_size > 0 ||
14282       player->inventory_infinite_element != EL_UNDEFINED)
14283   {
14284     if (player->inventory_size > 0)
14285     {
14286       player->inventory_size--;
14287
14288       DrawGameDoorValues();
14289
14290       if (new_element == EL_DYNAMITE)
14291         new_element = EL_DYNAMITE_ACTIVE;
14292       else if (new_element == EL_EM_DYNAMITE)
14293         new_element = EL_EM_DYNAMITE_ACTIVE;
14294       else if (new_element == EL_SP_DISK_RED)
14295         new_element = EL_SP_DISK_RED_ACTIVE;
14296     }
14297
14298     Feld[dropx][dropy] = new_element;
14299
14300     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14301       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14302                           el2img(Feld[dropx][dropy]), 0);
14303
14304     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14305
14306     /* needed if previous element just changed to "empty" in the last frame */
14307     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14308
14309     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14310                                player->index_bit, drop_side);
14311     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14312                                         CE_PLAYER_DROPS_X,
14313                                         player->index_bit, drop_side);
14314
14315     TestIfElementTouchesCustomElement(dropx, dropy);
14316   }
14317   else          /* player is dropping a dyna bomb */
14318   {
14319     player->dynabombs_left--;
14320
14321     Feld[dropx][dropy] = new_element;
14322
14323     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14324       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14325                           el2img(Feld[dropx][dropy]), 0);
14326
14327     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14328   }
14329
14330   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14331     InitField_WithBug1(dropx, dropy, FALSE);
14332
14333   new_element = Feld[dropx][dropy];     /* element might have changed */
14334
14335   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14336       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14337   {
14338     int move_direction, nextx, nexty;
14339
14340     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14341       MovDir[dropx][dropy] = drop_direction;
14342
14343     move_direction = MovDir[dropx][dropy];
14344     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14345     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14346
14347     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14348
14349 #if USE_FIX_IMPACT_COLLISION
14350     /* do not cause impact style collision by dropping elements that can fall */
14351     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14352 #else
14353     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14354 #endif
14355   }
14356
14357   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14358   player->is_dropping = TRUE;
14359
14360   player->drop_pressed_delay = 0;
14361   player->is_dropping_pressed = FALSE;
14362
14363   player->drop_x = dropx;
14364   player->drop_y = dropy;
14365
14366   return TRUE;
14367 }
14368
14369 /* ------------------------------------------------------------------------- */
14370 /* game sound playing functions                                              */
14371 /* ------------------------------------------------------------------------- */
14372
14373 static int *loop_sound_frame = NULL;
14374 static int *loop_sound_volume = NULL;
14375
14376 void InitPlayLevelSound()
14377 {
14378   int num_sounds = getSoundListSize();
14379
14380   checked_free(loop_sound_frame);
14381   checked_free(loop_sound_volume);
14382
14383   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14384   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14385 }
14386
14387 static void PlayLevelSound(int x, int y, int nr)
14388 {
14389   int sx = SCREENX(x), sy = SCREENY(y);
14390   int volume, stereo_position;
14391   int max_distance = 8;
14392   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14393
14394   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14395       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14396     return;
14397
14398   if (!IN_LEV_FIELD(x, y) ||
14399       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14400       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14401     return;
14402
14403   volume = SOUND_MAX_VOLUME;
14404
14405   if (!IN_SCR_FIELD(sx, sy))
14406   {
14407     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14408     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14409
14410     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14411   }
14412
14413   stereo_position = (SOUND_MAX_LEFT +
14414                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14415                      (SCR_FIELDX + 2 * max_distance));
14416
14417   if (IS_LOOP_SOUND(nr))
14418   {
14419     /* This assures that quieter loop sounds do not overwrite louder ones,
14420        while restarting sound volume comparison with each new game frame. */
14421
14422     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14423       return;
14424
14425     loop_sound_volume[nr] = volume;
14426     loop_sound_frame[nr] = FrameCounter;
14427   }
14428
14429   PlaySoundExt(nr, volume, stereo_position, type);
14430 }
14431
14432 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14433 {
14434   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14435                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14436                  y < LEVELY(BY1) ? LEVELY(BY1) :
14437                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14438                  sound_action);
14439 }
14440
14441 static void PlayLevelSoundAction(int x, int y, int action)
14442 {
14443   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14444 }
14445
14446 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14447 {
14448   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14449
14450   if (sound_effect != SND_UNDEFINED)
14451     PlayLevelSound(x, y, sound_effect);
14452 }
14453
14454 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14455                                               int action)
14456 {
14457   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14458
14459   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14460     PlayLevelSound(x, y, sound_effect);
14461 }
14462
14463 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14464 {
14465   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14466
14467   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14468     PlayLevelSound(x, y, sound_effect);
14469 }
14470
14471 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14472 {
14473   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14474
14475   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14476     StopSound(sound_effect);
14477 }
14478
14479 static void PlayLevelMusic()
14480 {
14481   if (levelset.music[level_nr] != MUS_UNDEFINED)
14482     PlayMusic(levelset.music[level_nr]);        /* from config file */
14483   else
14484     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14485 }
14486
14487 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14488 {
14489   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14490   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14491   int x = xx - 1 - offset;
14492   int y = yy - 1 - offset;
14493
14494   switch (sample)
14495   {
14496     case SAMPLE_blank:
14497       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14498       break;
14499
14500     case SAMPLE_roll:
14501       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14502       break;
14503
14504     case SAMPLE_stone:
14505       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14506       break;
14507
14508     case SAMPLE_nut:
14509       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14510       break;
14511
14512     case SAMPLE_crack:
14513       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14514       break;
14515
14516     case SAMPLE_bug:
14517       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14518       break;
14519
14520     case SAMPLE_tank:
14521       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14522       break;
14523
14524     case SAMPLE_android_clone:
14525       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14526       break;
14527
14528     case SAMPLE_android_move:
14529       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14530       break;
14531
14532     case SAMPLE_spring:
14533       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14534       break;
14535
14536     case SAMPLE_slurp:
14537       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14538       break;
14539
14540     case SAMPLE_eater:
14541       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14542       break;
14543
14544     case SAMPLE_eater_eat:
14545       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14546       break;
14547
14548     case SAMPLE_alien:
14549       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14550       break;
14551
14552     case SAMPLE_collect:
14553       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14554       break;
14555
14556     case SAMPLE_diamond:
14557       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14558       break;
14559
14560     case SAMPLE_squash:
14561       /* !!! CHECK THIS !!! */
14562 #if 1
14563       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14564 #else
14565       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14566 #endif
14567       break;
14568
14569     case SAMPLE_wonderfall:
14570       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14571       break;
14572
14573     case SAMPLE_drip:
14574       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14575       break;
14576
14577     case SAMPLE_push:
14578       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14579       break;
14580
14581     case SAMPLE_dirt:
14582       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14583       break;
14584
14585     case SAMPLE_acid:
14586       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14587       break;
14588
14589     case SAMPLE_ball:
14590       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14591       break;
14592
14593     case SAMPLE_grow:
14594       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14595       break;
14596
14597     case SAMPLE_wonder:
14598       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14599       break;
14600
14601     case SAMPLE_door:
14602       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14603       break;
14604
14605     case SAMPLE_exit_open:
14606       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14607       break;
14608
14609     case SAMPLE_exit_leave:
14610       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14611       break;
14612
14613     case SAMPLE_dynamite:
14614       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14615       break;
14616
14617     case SAMPLE_tick:
14618       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14619       break;
14620
14621     case SAMPLE_press:
14622       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14623       break;
14624
14625     case SAMPLE_wheel:
14626       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14627       break;
14628
14629     case SAMPLE_boom:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14631       break;
14632
14633     case SAMPLE_die:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14635       break;
14636
14637     case SAMPLE_time:
14638       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14639       break;
14640
14641     default:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14643       break;
14644   }
14645 }
14646
14647 #if 0
14648 void ChangeTime(int value)
14649 {
14650   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14651
14652   *time += value;
14653
14654   /* EMC game engine uses value from time counter of RND game engine */
14655   level.native_em_level->lev->time = *time;
14656
14657   DrawGameValue_Time(*time);
14658 }
14659
14660 void RaiseScore(int value)
14661 {
14662   /* EMC game engine and RND game engine have separate score counters */
14663   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14664                 &level.native_em_level->lev->score : &local_player->score);
14665
14666   *score += value;
14667
14668   DrawGameValue_Score(*score);
14669 }
14670 #endif
14671
14672 void RaiseScore(int value)
14673 {
14674   local_player->score += value;
14675
14676 #if 1
14677   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14678
14679   DisplayGameControlValues();
14680 #else
14681   DrawGameValue_Score(local_player->score);
14682 #endif
14683 }
14684
14685 void RaiseScoreElement(int element)
14686 {
14687   switch (element)
14688   {
14689     case EL_EMERALD:
14690     case EL_BD_DIAMOND:
14691     case EL_EMERALD_YELLOW:
14692     case EL_EMERALD_RED:
14693     case EL_EMERALD_PURPLE:
14694     case EL_SP_INFOTRON:
14695       RaiseScore(level.score[SC_EMERALD]);
14696       break;
14697     case EL_DIAMOND:
14698       RaiseScore(level.score[SC_DIAMOND]);
14699       break;
14700     case EL_CRYSTAL:
14701       RaiseScore(level.score[SC_CRYSTAL]);
14702       break;
14703     case EL_PEARL:
14704       RaiseScore(level.score[SC_PEARL]);
14705       break;
14706     case EL_BUG:
14707     case EL_BD_BUTTERFLY:
14708     case EL_SP_ELECTRON:
14709       RaiseScore(level.score[SC_BUG]);
14710       break;
14711     case EL_SPACESHIP:
14712     case EL_BD_FIREFLY:
14713     case EL_SP_SNIKSNAK:
14714       RaiseScore(level.score[SC_SPACESHIP]);
14715       break;
14716     case EL_YAMYAM:
14717     case EL_DARK_YAMYAM:
14718       RaiseScore(level.score[SC_YAMYAM]);
14719       break;
14720     case EL_ROBOT:
14721       RaiseScore(level.score[SC_ROBOT]);
14722       break;
14723     case EL_PACMAN:
14724       RaiseScore(level.score[SC_PACMAN]);
14725       break;
14726     case EL_NUT:
14727       RaiseScore(level.score[SC_NUT]);
14728       break;
14729     case EL_DYNAMITE:
14730     case EL_EM_DYNAMITE:
14731     case EL_SP_DISK_RED:
14732     case EL_DYNABOMB_INCREASE_NUMBER:
14733     case EL_DYNABOMB_INCREASE_SIZE:
14734     case EL_DYNABOMB_INCREASE_POWER:
14735       RaiseScore(level.score[SC_DYNAMITE]);
14736       break;
14737     case EL_SHIELD_NORMAL:
14738     case EL_SHIELD_DEADLY:
14739       RaiseScore(level.score[SC_SHIELD]);
14740       break;
14741     case EL_EXTRA_TIME:
14742       RaiseScore(level.extra_time_score);
14743       break;
14744     case EL_KEY_1:
14745     case EL_KEY_2:
14746     case EL_KEY_3:
14747     case EL_KEY_4:
14748     case EL_EM_KEY_1:
14749     case EL_EM_KEY_2:
14750     case EL_EM_KEY_3:
14751     case EL_EM_KEY_4:
14752     case EL_EMC_KEY_5:
14753     case EL_EMC_KEY_6:
14754     case EL_EMC_KEY_7:
14755     case EL_EMC_KEY_8:
14756     case EL_DC_KEY_WHITE:
14757       RaiseScore(level.score[SC_KEY]);
14758       break;
14759     default:
14760       RaiseScore(element_info[element].collect_score);
14761       break;
14762   }
14763 }
14764
14765 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14766 {
14767   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14768   {
14769 #if defined(NETWORK_AVALIABLE)
14770     if (options.network)
14771       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14772     else
14773 #endif
14774     {
14775       if (quick_quit)
14776       {
14777 #if 1
14778
14779 #if 1
14780         FadeSkipNextFadeIn();
14781 #else
14782         fading = fading_none;
14783 #endif
14784
14785 #else
14786         OpenDoor(DOOR_CLOSE_1);
14787 #endif
14788
14789         game_status = GAME_MODE_MAIN;
14790
14791 #if 1
14792         DrawAndFadeInMainMenu(REDRAW_FIELD);
14793 #else
14794         DrawMainMenu();
14795 #endif
14796       }
14797       else
14798       {
14799 #if 0
14800         FadeOut(REDRAW_FIELD);
14801 #endif
14802
14803         game_status = GAME_MODE_MAIN;
14804
14805         DrawAndFadeInMainMenu(REDRAW_FIELD);
14806       }
14807     }
14808   }
14809   else          /* continue playing the game */
14810   {
14811     if (tape.playing && tape.deactivate_display)
14812       TapeDeactivateDisplayOff(TRUE);
14813
14814     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14815
14816     if (tape.playing && tape.deactivate_display)
14817       TapeDeactivateDisplayOn();
14818   }
14819 }
14820
14821 void RequestQuitGame(boolean ask_if_really_quit)
14822 {
14823   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14824   boolean skip_request = AllPlayersGone || quick_quit;
14825
14826   RequestQuitGameExt(skip_request, quick_quit,
14827                      "Do you really want to quit the game ?");
14828 }
14829
14830
14831 /* ------------------------------------------------------------------------- */
14832 /* random generator functions                                                */
14833 /* ------------------------------------------------------------------------- */
14834
14835 unsigned int InitEngineRandom_RND(long seed)
14836 {
14837   game.num_random_calls = 0;
14838
14839 #if 0
14840   unsigned int rnd_seed = InitEngineRandom(seed);
14841
14842   printf("::: START RND: %d\n", rnd_seed);
14843
14844   return rnd_seed;
14845 #else
14846
14847   return InitEngineRandom(seed);
14848
14849 #endif
14850
14851 }
14852
14853 unsigned int RND(int max)
14854 {
14855   if (max > 0)
14856   {
14857     game.num_random_calls++;
14858
14859     return GetEngineRandom(max);
14860   }
14861
14862   return 0;
14863 }
14864
14865
14866 /* ------------------------------------------------------------------------- */
14867 /* game engine snapshot handling functions                                   */
14868 /* ------------------------------------------------------------------------- */
14869
14870 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
14871
14872 struct EngineSnapshotInfo
14873 {
14874   /* runtime values for custom element collect score */
14875   int collect_score[NUM_CUSTOM_ELEMENTS];
14876
14877   /* runtime values for group element choice position */
14878   int choice_pos[NUM_GROUP_ELEMENTS];
14879
14880   /* runtime values for belt position animations */
14881   int belt_graphic[4 * NUM_BELT_PARTS];
14882   int belt_anim_mode[4 * NUM_BELT_PARTS];
14883 };
14884
14885 struct EngineSnapshotNodeInfo
14886 {
14887   void *buffer_orig;
14888   void *buffer_copy;
14889   int size;
14890 };
14891
14892 static struct EngineSnapshotInfo engine_snapshot_rnd;
14893 static ListNode *engine_snapshot_list = NULL;
14894 static char *snapshot_level_identifier = NULL;
14895 static int snapshot_level_nr = -1;
14896
14897 void FreeEngineSnapshot()
14898 {
14899   while (engine_snapshot_list != NULL)
14900     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
14901                        checked_free);
14902
14903   setString(&snapshot_level_identifier, NULL);
14904   snapshot_level_nr = -1;
14905 }
14906
14907 static void SaveEngineSnapshotValues_RND()
14908 {
14909   static int belt_base_active_element[4] =
14910   {
14911     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14912     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14913     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14914     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14915   };
14916   int i, j;
14917
14918   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14919   {
14920     int element = EL_CUSTOM_START + i;
14921
14922     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14923   }
14924
14925   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14926   {
14927     int element = EL_GROUP_START + i;
14928
14929     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14930   }
14931
14932   for (i = 0; i < 4; i++)
14933   {
14934     for (j = 0; j < NUM_BELT_PARTS; j++)
14935     {
14936       int element = belt_base_active_element[i] + j;
14937       int graphic = el2img(element);
14938       int anim_mode = graphic_info[graphic].anim_mode;
14939
14940       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
14941       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
14942     }
14943   }
14944 }
14945
14946 static void LoadEngineSnapshotValues_RND()
14947 {
14948   unsigned long num_random_calls = game.num_random_calls;
14949   int i, j;
14950
14951   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14952   {
14953     int element = EL_CUSTOM_START + i;
14954
14955     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14956   }
14957
14958   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14959   {
14960     int element = EL_GROUP_START + i;
14961
14962     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14963   }
14964
14965   for (i = 0; i < 4; i++)
14966   {
14967     for (j = 0; j < NUM_BELT_PARTS; j++)
14968     {
14969       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
14970       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
14971
14972       graphic_info[graphic].anim_mode = anim_mode;
14973     }
14974   }
14975
14976   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14977   {
14978     InitRND(tape.random_seed);
14979     for (i = 0; i < num_random_calls; i++)
14980       RND(1);
14981   }
14982
14983   if (game.num_random_calls != num_random_calls)
14984   {
14985     Error(ERR_INFO, "number of random calls out of sync");
14986     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14987     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14988     Error(ERR_EXIT, "this should not happen -- please debug");
14989   }
14990 }
14991
14992 static void SaveEngineSnapshotBuffer(void *buffer, int size)
14993 {
14994   struct EngineSnapshotNodeInfo *bi =
14995     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
14996
14997   bi->buffer_orig = buffer;
14998   bi->buffer_copy = checked_malloc(size);
14999   bi->size = size;
15000
15001   memcpy(bi->buffer_copy, buffer, size);
15002
15003   addNodeToList(&engine_snapshot_list, NULL, bi);
15004 }
15005
15006 void SaveEngineSnapshot()
15007 {
15008   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15009
15010   if (level_editor_test_game)   /* do not save snapshots from editor */
15011     return;
15012
15013   /* copy some special values to a structure better suited for the snapshot */
15014
15015   SaveEngineSnapshotValues_RND();
15016   SaveEngineSnapshotValues_EM();
15017
15018   /* save values stored in special snapshot structure */
15019
15020   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15021   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15022
15023   /* save further RND engine values */
15024
15025   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15026   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15027   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15028
15029   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15030   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15031   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15032   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15033
15034   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15035   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15036   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15037   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15038   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15039
15040   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15041   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15042   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15043
15044   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15045
15046   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15047
15048   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15049   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15050
15051   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15052   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15053   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15054   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15055   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15056   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15057   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15058   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15059   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15060   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15061   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15062   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15063   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15064   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15065   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15066   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15067   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15068   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15069
15070   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15071   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15072
15073   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15074   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15075   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15076
15077   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15078   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15079
15080   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15081   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15082   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15083   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15084   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15085
15086   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15087   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15088
15089   /* save level identification information */
15090
15091   setString(&snapshot_level_identifier, leveldir_current->identifier);
15092   snapshot_level_nr = level_nr;
15093
15094 #if 0
15095   ListNode *node = engine_snapshot_list;
15096   int num_bytes = 0;
15097
15098   while (node != NULL)
15099   {
15100     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15101
15102     node = node->next;
15103   }
15104
15105   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15106 #endif
15107 }
15108
15109 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15110 {
15111   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15112 }
15113
15114 void LoadEngineSnapshot()
15115 {
15116   ListNode *node = engine_snapshot_list;
15117
15118   if (engine_snapshot_list == NULL)
15119     return;
15120
15121   while (node != NULL)
15122   {
15123     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15124
15125     node = node->next;
15126   }
15127
15128   /* restore special values from snapshot structure */
15129
15130   LoadEngineSnapshotValues_RND();
15131   LoadEngineSnapshotValues_EM();
15132 }
15133
15134 boolean CheckEngineSnapshot()
15135 {
15136   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15137           snapshot_level_nr == level_nr);
15138 }
15139
15140
15141 /* ---------- new game button stuff ---------------------------------------- */
15142
15143 /* graphic position values for game buttons */
15144 #define GAME_BUTTON_XSIZE       30
15145 #define GAME_BUTTON_YSIZE       30
15146 #define GAME_BUTTON_XPOS        5
15147 #define GAME_BUTTON_YPOS        215
15148 #define SOUND_BUTTON_XPOS       5
15149 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15150
15151 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15152 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15153 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15154 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15155 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15156 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15157
15158 static struct
15159 {
15160   int *x, *y;
15161   int gd_x, gd_y;
15162   int gadget_id;
15163   char *infotext;
15164 } gamebutton_info[NUM_GAME_BUTTONS] =
15165 {
15166 #if 1
15167   {
15168     &game.button.stop.x,        &game.button.stop.y,
15169     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15170     GAME_CTRL_ID_STOP,
15171     "stop game"
15172   },
15173   {
15174     &game.button.pause.x,       &game.button.pause.y,
15175     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15176     GAME_CTRL_ID_PAUSE,
15177     "pause game"
15178   },
15179   {
15180     &game.button.play.x,        &game.button.play.y,
15181     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15182     GAME_CTRL_ID_PLAY,
15183     "play game"
15184   },
15185   {
15186     &game.button.sound_music.x, &game.button.sound_music.y,
15187     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15188     SOUND_CTRL_ID_MUSIC,
15189     "background music on/off"
15190   },
15191   {
15192     &game.button.sound_loops.x, &game.button.sound_loops.y,
15193     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15194     SOUND_CTRL_ID_LOOPS,
15195     "sound loops on/off"
15196   },
15197   {
15198     &game.button.sound_simple.x,&game.button.sound_simple.y,
15199     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15200     SOUND_CTRL_ID_SIMPLE,
15201     "normal sounds on/off"
15202   }
15203 #else
15204   {
15205     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15206     GAME_CTRL_ID_STOP,
15207     "stop game"
15208   },
15209   {
15210     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15211     GAME_CTRL_ID_PAUSE,
15212     "pause game"
15213   },
15214   {
15215     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15216     GAME_CTRL_ID_PLAY,
15217     "play game"
15218   },
15219   {
15220     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15221     SOUND_CTRL_ID_MUSIC,
15222     "background music on/off"
15223   },
15224   {
15225     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15226     SOUND_CTRL_ID_LOOPS,
15227     "sound loops on/off"
15228   },
15229   {
15230     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15231     SOUND_CTRL_ID_SIMPLE,
15232     "normal sounds on/off"
15233   }
15234 #endif
15235 };
15236
15237 void CreateGameButtons()
15238 {
15239   int i;
15240
15241   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15242   {
15243     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15244     struct GadgetInfo *gi;
15245     int button_type;
15246     boolean checked;
15247     unsigned long event_mask;
15248     int x, y;
15249     int gd_xoffset, gd_yoffset;
15250     int gd_x1, gd_x2, gd_y1, gd_y2;
15251     int id = i;
15252
15253     x = DX + *gamebutton_info[i].x;
15254     y = DY + *gamebutton_info[i].y;
15255     gd_xoffset = gamebutton_info[i].gd_x;
15256     gd_yoffset = gamebutton_info[i].gd_y;
15257     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15258     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15259
15260     if (id == GAME_CTRL_ID_STOP ||
15261         id == GAME_CTRL_ID_PAUSE ||
15262         id == GAME_CTRL_ID_PLAY)
15263     {
15264       button_type = GD_TYPE_NORMAL_BUTTON;
15265       checked = FALSE;
15266       event_mask = GD_EVENT_RELEASED;
15267       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15268       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15269     }
15270     else
15271     {
15272       button_type = GD_TYPE_CHECK_BUTTON;
15273       checked =
15274         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15275          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15276          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15277       event_mask = GD_EVENT_PRESSED;
15278       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15279       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15280     }
15281
15282     gi = CreateGadget(GDI_CUSTOM_ID, id,
15283                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15284 #if 1
15285                       GDI_X, x,
15286                       GDI_Y, y,
15287 #else
15288                       GDI_X, DX + gd_xoffset,
15289                       GDI_Y, DY + gd_yoffset,
15290 #endif
15291                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15292                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15293                       GDI_TYPE, button_type,
15294                       GDI_STATE, GD_BUTTON_UNPRESSED,
15295                       GDI_CHECKED, checked,
15296                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15297                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15298                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15299                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15300                       GDI_EVENT_MASK, event_mask,
15301                       GDI_CALLBACK_ACTION, HandleGameButtons,
15302                       GDI_END);
15303
15304     if (gi == NULL)
15305       Error(ERR_EXIT, "cannot create gadget");
15306
15307     game_gadget[id] = gi;
15308   }
15309 }
15310
15311 void FreeGameButtons()
15312 {
15313   int i;
15314
15315   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15316     FreeGadget(game_gadget[i]);
15317 }
15318
15319 static void MapGameButtons()
15320 {
15321   int i;
15322
15323   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15324     MapGadget(game_gadget[i]);
15325 }
15326
15327 void UnmapGameButtons()
15328 {
15329   int i;
15330
15331   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15332     UnmapGadget(game_gadget[i]);
15333 }
15334
15335 static void HandleGameButtons(struct GadgetInfo *gi)
15336 {
15337   int id = gi->custom_id;
15338
15339   if (game_status != GAME_MODE_PLAYING)
15340     return;
15341
15342   switch (id)
15343   {
15344     case GAME_CTRL_ID_STOP:
15345       if (tape.playing)
15346         TapeStop();
15347       else
15348         RequestQuitGame(TRUE);
15349       break;
15350
15351     case GAME_CTRL_ID_PAUSE:
15352       if (options.network)
15353       {
15354 #if defined(NETWORK_AVALIABLE)
15355         if (tape.pausing)
15356           SendToServer_ContinuePlaying();
15357         else
15358           SendToServer_PausePlaying();
15359 #endif
15360       }
15361       else
15362         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15363       break;
15364
15365     case GAME_CTRL_ID_PLAY:
15366       if (tape.pausing)
15367       {
15368 #if defined(NETWORK_AVALIABLE)
15369         if (options.network)
15370           SendToServer_ContinuePlaying();
15371         else
15372 #endif
15373         {
15374           tape.pausing = FALSE;
15375           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15376         }
15377       }
15378       break;
15379
15380     case SOUND_CTRL_ID_MUSIC:
15381       if (setup.sound_music)
15382       { 
15383         setup.sound_music = FALSE;
15384         FadeMusic();
15385       }
15386       else if (audio.music_available)
15387       { 
15388         setup.sound = setup.sound_music = TRUE;
15389
15390         SetAudioMode(setup.sound);
15391
15392         PlayLevelMusic();
15393       }
15394       break;
15395
15396     case SOUND_CTRL_ID_LOOPS:
15397       if (setup.sound_loops)
15398         setup.sound_loops = FALSE;
15399       else if (audio.loops_available)
15400       {
15401         setup.sound = setup.sound_loops = TRUE;
15402         SetAudioMode(setup.sound);
15403       }
15404       break;
15405
15406     case SOUND_CTRL_ID_SIMPLE:
15407       if (setup.sound_simple)
15408         setup.sound_simple = FALSE;
15409       else if (audio.sound_available)
15410       {
15411         setup.sound = setup.sound_simple = TRUE;
15412         SetAudioMode(setup.sound);
15413       }
15414       break;
15415
15416     default:
15417       break;
15418   }
15419 }