0d748e5b5330707b58f95191ce497807248ac60b
[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_2              59
194 #define GAME_PANEL_CONVEYOR_BELT_3              60
195 #define GAME_PANEL_CONVEYOR_BELT_4              61
196 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       62
197 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       63
198 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       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_CE_SCORE_1                   69
204 #define GAME_PANEL_CE_SCORE_2                   70
205 #define GAME_PANEL_CE_SCORE_3                   71
206 #define GAME_PANEL_CE_SCORE_4                   72
207 #define GAME_PANEL_CE_SCORE_5                   73
208 #define GAME_PANEL_CE_SCORE_6                   74
209 #define GAME_PANEL_CE_SCORE_7                   75
210 #define GAME_PANEL_CE_SCORE_8                   76
211 #define GAME_PANEL_CE_SCORE_1_ELEMENT           77
212 #define GAME_PANEL_CE_SCORE_2_ELEMENT           78
213 #define GAME_PANEL_CE_SCORE_3_ELEMENT           79
214 #define GAME_PANEL_CE_SCORE_4_ELEMENT           80
215 #define GAME_PANEL_CE_SCORE_5_ELEMENT           81
216 #define GAME_PANEL_CE_SCORE_6_ELEMENT           82
217 #define GAME_PANEL_CE_SCORE_7_ELEMENT           83
218 #define GAME_PANEL_CE_SCORE_8_ELEMENT           84
219 #define GAME_PANEL_PLAYER_NAME                  85
220 #define GAME_PANEL_LEVEL_NAME                   86
221 #define GAME_PANEL_LEVEL_AUTHOR                 87
222
223 #define NUM_GAME_PANEL_CONTROLS                 88
224
225 struct GamePanelOrderInfo
226 {
227   int nr;
228   int sort_priority;
229 };
230
231 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
232
233 struct GamePanelControlInfo
234 {
235   int nr;
236
237   struct TextPosInfo *pos;
238   int type;
239
240   int value, last_value;
241   int frame, last_frame;
242   int gfx_frame;
243 };
244
245 static struct GamePanelControlInfo game_panel_controls[] =
246 {
247   {
248     GAME_PANEL_LEVEL_NUMBER,
249     &game.panel.level_number,
250     TYPE_INTEGER,
251   },
252   {
253     GAME_PANEL_GEMS,
254     &game.panel.gems,
255     TYPE_INTEGER,
256   },
257   {
258     GAME_PANEL_INVENTORY_COUNT,
259     &game.panel.inventory_count,
260     TYPE_INTEGER,
261   },
262   {
263     GAME_PANEL_INVENTORY_FIRST_1,
264     &game.panel.inventory_first[0],
265     TYPE_ELEMENT,
266   },
267   {
268     GAME_PANEL_INVENTORY_FIRST_2,
269     &game.panel.inventory_first[1],
270     TYPE_ELEMENT,
271   },
272   {
273     GAME_PANEL_INVENTORY_FIRST_3,
274     &game.panel.inventory_first[2],
275     TYPE_ELEMENT,
276   },
277   {
278     GAME_PANEL_INVENTORY_FIRST_4,
279     &game.panel.inventory_first[3],
280     TYPE_ELEMENT,
281   },
282   {
283     GAME_PANEL_INVENTORY_FIRST_5,
284     &game.panel.inventory_first[4],
285     TYPE_ELEMENT,
286   },
287   {
288     GAME_PANEL_INVENTORY_FIRST_6,
289     &game.panel.inventory_first[5],
290     TYPE_ELEMENT,
291   },
292   {
293     GAME_PANEL_INVENTORY_FIRST_7,
294     &game.panel.inventory_first[6],
295     TYPE_ELEMENT,
296   },
297   {
298     GAME_PANEL_INVENTORY_FIRST_8,
299     &game.panel.inventory_first[7],
300     TYPE_ELEMENT,
301   },
302   {
303     GAME_PANEL_INVENTORY_LAST_1,
304     &game.panel.inventory_last[0],
305     TYPE_ELEMENT,
306   },
307   {
308     GAME_PANEL_INVENTORY_LAST_2,
309     &game.panel.inventory_last[1],
310     TYPE_ELEMENT,
311   },
312   {
313     GAME_PANEL_INVENTORY_LAST_3,
314     &game.panel.inventory_last[2],
315     TYPE_ELEMENT,
316   },
317   {
318     GAME_PANEL_INVENTORY_LAST_4,
319     &game.panel.inventory_last[3],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_LAST_5,
324     &game.panel.inventory_last[4],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_LAST_6,
329     &game.panel.inventory_last[5],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_LAST_7,
334     &game.panel.inventory_last[6],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_LAST_8,
339     &game.panel.inventory_last[7],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_KEY_1,
344     &game.panel.key[0],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_KEY_2,
349     &game.panel.key[1],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_KEY_3,
354     &game.panel.key[2],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_KEY_4,
359     &game.panel.key[3],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_KEY_5,
364     &game.panel.key[4],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_KEY_6,
369     &game.panel.key[5],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_KEY_7,
374     &game.panel.key[6],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_KEY_8,
379     &game.panel.key[7],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_KEY_WHITE,
384     &game.panel.key_white,
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_KEY_WHITE_COUNT,
389     &game.panel.key_white_count,
390     TYPE_INTEGER,
391   },
392   {
393     GAME_PANEL_SCORE,
394     &game.panel.score,
395     TYPE_INTEGER,
396   },
397   {
398     GAME_PANEL_TIME,
399     &game.panel.time,
400     TYPE_INTEGER,
401   },
402   {
403     GAME_PANEL_TIME_HH,
404     &game.panel.time_hh,
405     TYPE_INTEGER,
406   },
407   {
408     GAME_PANEL_TIME_MM,
409     &game.panel.time_mm,
410     TYPE_INTEGER,
411   },
412   {
413     GAME_PANEL_TIME_SS,
414     &game.panel.time_ss,
415     TYPE_INTEGER,
416   },
417   {
418     GAME_PANEL_SHIELD_NORMAL,
419     &game.panel.shield_normal,
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_SHIELD_NORMAL_TIME,
424     &game.panel.shield_normal_time,
425     TYPE_INTEGER,
426   },
427   {
428     GAME_PANEL_SHIELD_DEADLY,
429     &game.panel.shield_deadly,
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_SHIELD_DEADLY_TIME,
434     &game.panel.shield_deadly_time,
435     TYPE_INTEGER,
436   },
437   {
438     GAME_PANEL_EXIT,
439     &game.panel.exit,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_EMC_MAGIC_BALL,
444     &game.panel.emc_magic_ball,
445     TYPE_ELEMENT,
446   },
447   {
448     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
449     &game.panel.emc_magic_ball_switch,
450     TYPE_ELEMENT,
451   },
452   {
453     GAME_PANEL_LIGHT_SWITCH,
454     &game.panel.light_switch,
455     TYPE_ELEMENT,
456   },
457   {
458     GAME_PANEL_LIGHT_SWITCH_TIME,
459     &game.panel.light_switch_time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIMEGATE_SWITCH,
464     &game.panel.timegate_switch,
465     TYPE_ELEMENT,
466   },
467   {
468     GAME_PANEL_TIMEGATE_SWITCH_TIME,
469     &game.panel.timegate_switch_time,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_SWITCHGATE_SWITCH,
474     &game.panel.switchgate_switch,
475     TYPE_ELEMENT,
476   },
477   {
478     GAME_PANEL_EMC_LENSES,
479     &game.panel.emc_lenses,
480     TYPE_ELEMENT,
481   },
482   {
483     GAME_PANEL_EMC_LENSES_TIME,
484     &game.panel.emc_lenses_time,
485     TYPE_INTEGER,
486   },
487   {
488     GAME_PANEL_EMC_MAGNIFIER,
489     &game.panel.emc_magnifier,
490     TYPE_ELEMENT,
491   },
492   {
493     GAME_PANEL_EMC_MAGNIFIER_TIME,
494     &game.panel.emc_magnifier_time,
495     TYPE_INTEGER,
496   },
497   {
498     GAME_PANEL_BALLOON_SWITCH,
499     &game.panel.balloon_switch,
500     TYPE_ELEMENT,
501   },
502   {
503     GAME_PANEL_DYNABOMB_NUMBER,
504     &game.panel.dynabomb_number,
505     TYPE_INTEGER,
506   },
507   {
508     GAME_PANEL_DYNABOMB_SIZE,
509     &game.panel.dynabomb_size,
510     TYPE_INTEGER,
511   },
512   {
513     GAME_PANEL_DYNABOMB_POWER,
514     &game.panel.dynabomb_power,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_PENGUINS,
519     &game.panel.penguins,
520     TYPE_INTEGER,
521   },
522   {
523     GAME_PANEL_SOKOBAN_OBJECTS,
524     &game.panel.sokoban_objects,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_SOKOBAN_FIELDS,
529     &game.panel.sokoban_fields,
530     TYPE_INTEGER,
531   },
532   {
533     GAME_PANEL_ROBOT_WHEEL,
534     &game.panel.robot_wheel,
535     TYPE_ELEMENT,
536   },
537   {
538     GAME_PANEL_CONVEYOR_BELT_1,
539     &game.panel.conveyor_belt[0],
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_CONVEYOR_BELT_2,
544     &game.panel.conveyor_belt[1],
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_CONVEYOR_BELT_3,
549     &game.panel.conveyor_belt[2],
550     TYPE_ELEMENT,
551   },
552   {
553     GAME_PANEL_CONVEYOR_BELT_4,
554     &game.panel.conveyor_belt[3],
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
559     &game.panel.conveyor_belt_switch[0],
560     TYPE_ELEMENT,
561   },
562   {
563     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
564     &game.panel.conveyor_belt_switch[1],
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
569     &game.panel.conveyor_belt_switch[2],
570     TYPE_ELEMENT,
571   },
572   {
573     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
574     &game.panel.conveyor_belt_switch[3],
575     TYPE_ELEMENT,
576   },
577   {
578     GAME_PANEL_MAGIC_WALL,
579     &game.panel.magic_wall,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_MAGIC_WALL_TIME,
584     &game.panel.magic_wall_time,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_GRAVITY_STATE,
589     &game.panel.gravity_state,
590     TYPE_STRING,
591   },
592   {
593     GAME_PANEL_CE_SCORE_1,
594     &game.panel.ce_score[0],
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_CE_SCORE_2,
599     &game.panel.ce_score[1],
600     TYPE_INTEGER,
601   },
602   {
603     GAME_PANEL_CE_SCORE_3,
604     &game.panel.ce_score[2],
605     TYPE_INTEGER,
606   },
607   {
608     GAME_PANEL_CE_SCORE_4,
609     &game.panel.ce_score[3],
610     TYPE_INTEGER,
611   },
612   {
613     GAME_PANEL_CE_SCORE_5,
614     &game.panel.ce_score[4],
615     TYPE_INTEGER,
616   },
617   {
618     GAME_PANEL_CE_SCORE_6,
619     &game.panel.ce_score[5],
620     TYPE_INTEGER,
621   },
622   {
623     GAME_PANEL_CE_SCORE_7,
624     &game.panel.ce_score[6],
625     TYPE_INTEGER,
626   },
627   {
628     GAME_PANEL_CE_SCORE_8,
629     &game.panel.ce_score[7],
630     TYPE_INTEGER,
631   },
632   {
633     GAME_PANEL_CE_SCORE_1_ELEMENT,
634     &game.panel.ce_score_element[0],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CE_SCORE_2_ELEMENT,
639     &game.panel.ce_score_element[1],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_CE_SCORE_3_ELEMENT,
644     &game.panel.ce_score_element[2],
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_CE_SCORE_4_ELEMENT,
649     &game.panel.ce_score_element[3],
650     TYPE_ELEMENT,
651   },
652   {
653     GAME_PANEL_CE_SCORE_5_ELEMENT,
654     &game.panel.ce_score_element[4],
655     TYPE_ELEMENT,
656   },
657   {
658     GAME_PANEL_CE_SCORE_6_ELEMENT,
659     &game.panel.ce_score_element[5],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_CE_SCORE_7_ELEMENT,
664     &game.panel.ce_score_element[6],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_CE_SCORE_8_ELEMENT,
669     &game.panel.ce_score_element[7],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_PLAYER_NAME,
674     &game.panel.player_name,
675     TYPE_STRING,
676   },
677   {
678     GAME_PANEL_LEVEL_NAME,
679     &game.panel.level_name,
680     TYPE_STRING,
681   },
682   {
683     GAME_PANEL_LEVEL_AUTHOR,
684     &game.panel.level_author,
685     TYPE_STRING,
686   },
687
688   {
689     -1,
690     NULL,
691     -1,
692   }
693 };
694 #endif
695
696
697 /* values for delayed check of falling and moving elements and for collision */
698 #define CHECK_DELAY_MOVING      3
699 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
700 #define CHECK_DELAY_COLLISION   2
701 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
702
703 /* values for initial player move delay (initial delay counter value) */
704 #define INITIAL_MOVE_DELAY_OFF  -1
705 #define INITIAL_MOVE_DELAY_ON   0
706
707 /* values for player movement speed (which is in fact a delay value) */
708 #define MOVE_DELAY_MIN_SPEED    32
709 #define MOVE_DELAY_NORMAL_SPEED 8
710 #define MOVE_DELAY_HIGH_SPEED   4
711 #define MOVE_DELAY_MAX_SPEED    1
712
713 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
714 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
715
716 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
717 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
718
719 /* values for other actions */
720 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
721 #define MOVE_STEPSIZE_MIN       (1)
722 #define MOVE_STEPSIZE_MAX       (TILEX)
723
724 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
725 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
726
727 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
728
729 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
730                                  RND(element_info[e].push_delay_random))
731 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
732                                  RND(element_info[e].drop_delay_random))
733 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
734                                  RND(element_info[e].move_delay_random))
735 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
736                                     (element_info[e].move_delay_random))
737 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
738                                  RND(element_info[e].ce_value_random_initial))
739 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
740 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
741                                  RND((c)->delay_random * (c)->delay_frames))
742 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
743                                  RND((c)->delay_random))
744
745
746 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
747          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
748
749 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
750         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
751          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
752          (be) + (e) - EL_SELF)
753
754 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
755         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
756          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
757          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
758          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
759          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
760          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
761          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
762          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
763          (e))
764
765 #define CAN_GROW_INTO(e)                                                \
766         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
767
768 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
769                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
770                                         (condition)))
771
772 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
773                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
774                                         (CAN_MOVE_INTO_ACID(e) &&       \
775                                          Feld[x][y] == EL_ACID) ||      \
776                                         (condition)))
777
778 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
779                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
780                                         (CAN_MOVE_INTO_ACID(e) &&       \
781                                          Feld[x][y] == EL_ACID) ||      \
782                                         (condition)))
783
784 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
785                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
786                                         (condition) ||                  \
787                                         (CAN_MOVE_INTO_ACID(e) &&       \
788                                          Feld[x][y] == EL_ACID) ||      \
789                                         (DONT_COLLIDE_WITH(e) &&        \
790                                          IS_PLAYER(x, y) &&             \
791                                          !PLAYER_ENEMY_PROTECTED(x, y))))
792
793 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
794         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
795
796 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
797         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
798
799 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
800         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
801
802 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
803         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
804                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
805
806 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
807         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
808
809 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
810         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
811
812 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
813         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
814
815 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
816         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
817
818 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
819         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
820
821 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
822         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
823                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
824                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
825                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
826                                                  IS_FOOD_PENGUIN(Feld[x][y])))
827 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
828         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
829
830 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
831         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
832
833 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
834         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
835
836 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
837         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
838                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
839
840 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
841
842 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
843                 (!IS_PLAYER(x, y) &&                                    \
844                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
845
846 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
847         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
848
849 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
850 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
851
852 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
853 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
854 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
855 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
856
857 /* game button identifiers */
858 #define GAME_CTRL_ID_STOP               0
859 #define GAME_CTRL_ID_PAUSE              1
860 #define GAME_CTRL_ID_PLAY               2
861 #define SOUND_CTRL_ID_MUSIC             3
862 #define SOUND_CTRL_ID_LOOPS             4
863 #define SOUND_CTRL_ID_SIMPLE            5
864
865 #define NUM_GAME_BUTTONS                6
866
867
868 /* forward declaration for internal use */
869
870 static void CreateField(int, int, int);
871
872 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
873 static void AdvanceFrameAndPlayerCounters(int);
874
875 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
876 static boolean MovePlayer(struct PlayerInfo *, int, int);
877 static void ScrollPlayer(struct PlayerInfo *, int);
878 static void ScrollScreen(struct PlayerInfo *, int);
879
880 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
881
882 static void InitBeltMovement(void);
883 static void CloseAllOpenTimegates(void);
884 static void CheckGravityMovement(struct PlayerInfo *);
885 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
886 static void KillPlayerUnlessEnemyProtected(int, int);
887 static void KillPlayerUnlessExplosionProtected(int, int);
888
889 static void TestIfPlayerTouchesCustomElement(int, int);
890 static void TestIfElementTouchesCustomElement(int, int);
891 static void TestIfElementHitsCustomElement(int, int, int);
892 #if 0
893 static void TestIfElementSmashesCustomElement(int, int, int);
894 #endif
895
896 static void HandleElementChange(int, int, int);
897 static void ExecuteCustomElementAction(int, int, int, int);
898 static boolean ChangeElement(int, int, int, int);
899
900 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
901 #define CheckTriggeredElementChange(x, y, e, ev)                        \
902         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
903 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
904         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
905 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
906         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
907 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
908         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
909
910 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
911 #define CheckElementChange(x, y, e, te, ev)                             \
912         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
913 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
914         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
915 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
916         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
917
918 static void PlayLevelSound(int, int, int);
919 static void PlayLevelSoundNearest(int, int, int);
920 static void PlayLevelSoundAction(int, int, int);
921 static void PlayLevelSoundElementAction(int, int, int, int);
922 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
923 static void PlayLevelSoundActionIfLoop(int, int, int);
924 static void StopLevelSoundActionIfLoop(int, int, int);
925 static void PlayLevelMusic();
926
927 static void MapGameButtons();
928 static void HandleGameButtons(struct GadgetInfo *);
929
930 int AmoebeNachbarNr(int, int);
931 void AmoebeUmwandeln(int, int);
932 void ContinueMoving(int, int);
933 void Bang(int, int);
934 void InitMovDir(int, int);
935 void InitAmoebaNr(int, int);
936 int NewHiScore(void);
937
938 void TestIfGoodThingHitsBadThing(int, int, int);
939 void TestIfBadThingHitsGoodThing(int, int, int);
940 void TestIfPlayerTouchesBadThing(int, int);
941 void TestIfPlayerRunsIntoBadThing(int, int, int);
942 void TestIfBadThingTouchesPlayer(int, int);
943 void TestIfBadThingRunsIntoPlayer(int, int, int);
944 void TestIfFriendTouchesBadThing(int, int);
945 void TestIfBadThingTouchesFriend(int, int);
946 void TestIfBadThingTouchesOtherBadThing(int, int);
947
948 void KillPlayer(struct PlayerInfo *);
949 void BuryPlayer(struct PlayerInfo *);
950 void RemovePlayer(struct PlayerInfo *);
951
952 boolean SnapField(struct PlayerInfo *, int, int);
953 boolean DropElement(struct PlayerInfo *);
954
955 static int getInvisibleActiveFromInvisibleElement(int);
956 static int getInvisibleFromInvisibleActiveElement(int);
957
958 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
959
960 /* for detection of endless loops, caused by custom element programming */
961 /* (using maximal playfield width x 10 is just a rough approximation) */
962 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
963
964 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
965 {                                                                       \
966   if (recursion_loop_detected)                                          \
967     return (rc);                                                        \
968                                                                         \
969   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
970   {                                                                     \
971     recursion_loop_detected = TRUE;                                     \
972     recursion_loop_element = (e);                                       \
973   }                                                                     \
974                                                                         \
975   recursion_loop_depth++;                                               \
976 }
977
978 #define RECURSION_LOOP_DETECTION_END()                                  \
979 {                                                                       \
980   recursion_loop_depth--;                                               \
981 }
982
983 static int recursion_loop_depth;
984 static boolean recursion_loop_detected;
985 static boolean recursion_loop_element;
986
987
988 /* ------------------------------------------------------------------------- */
989 /* definition of elements that automatically change to other elements after  */
990 /* a specified time, eventually calling a function when changing             */
991 /* ------------------------------------------------------------------------- */
992
993 /* forward declaration for changer functions */
994 static void InitBuggyBase(int, int);
995 static void WarnBuggyBase(int, int);
996
997 static void InitTrap(int, int);
998 static void ActivateTrap(int, int);
999 static void ChangeActiveTrap(int, int);
1000
1001 static void InitRobotWheel(int, int);
1002 static void RunRobotWheel(int, int);
1003 static void StopRobotWheel(int, int);
1004
1005 static void InitTimegateWheel(int, int);
1006 static void RunTimegateWheel(int, int);
1007
1008 static void InitMagicBallDelay(int, int);
1009 static void ActivateMagicBall(int, int);
1010
1011 struct ChangingElementInfo
1012 {
1013   int element;
1014   int target_element;
1015   int change_delay;
1016   void (*pre_change_function)(int x, int y);
1017   void (*change_function)(int x, int y);
1018   void (*post_change_function)(int x, int y);
1019 };
1020
1021 static struct ChangingElementInfo change_delay_list[] =
1022 {
1023   {
1024     EL_NUT_BREAKING,
1025     EL_EMERALD,
1026     6,
1027     NULL,
1028     NULL,
1029     NULL
1030   },
1031   {
1032     EL_PEARL_BREAKING,
1033     EL_EMPTY,
1034     8,
1035     NULL,
1036     NULL,
1037     NULL
1038   },
1039   {
1040     EL_EXIT_OPENING,
1041     EL_EXIT_OPEN,
1042     29,
1043     NULL,
1044     NULL,
1045     NULL
1046   },
1047   {
1048     EL_EXIT_CLOSING,
1049     EL_EXIT_CLOSED,
1050     29,
1051     NULL,
1052     NULL,
1053     NULL
1054   },
1055   {
1056     EL_STEEL_EXIT_OPENING,
1057     EL_STEEL_EXIT_OPEN,
1058     29,
1059     NULL,
1060     NULL,
1061     NULL
1062   },
1063   {
1064     EL_STEEL_EXIT_CLOSING,
1065     EL_STEEL_EXIT_CLOSED,
1066     29,
1067     NULL,
1068     NULL,
1069     NULL
1070   },
1071   {
1072     EL_EM_EXIT_OPENING,
1073     EL_EM_EXIT_OPEN,
1074     29,
1075     NULL,
1076     NULL,
1077     NULL
1078   },
1079   {
1080     EL_EM_EXIT_CLOSING,
1081 #if 1
1082     EL_EMPTY,
1083 #else
1084     EL_EM_EXIT_CLOSED,
1085 #endif
1086     29,
1087     NULL,
1088     NULL,
1089     NULL
1090   },
1091   {
1092     EL_EM_STEEL_EXIT_OPENING,
1093     EL_EM_STEEL_EXIT_OPEN,
1094     29,
1095     NULL,
1096     NULL,
1097     NULL
1098   },
1099   {
1100     EL_EM_STEEL_EXIT_CLOSING,
1101 #if 1
1102     EL_STEELWALL,
1103 #else
1104     EL_EM_STEEL_EXIT_CLOSED,
1105 #endif
1106     29,
1107     NULL,
1108     NULL,
1109     NULL
1110   },
1111   {
1112     EL_SP_EXIT_OPENING,
1113     EL_SP_EXIT_OPEN,
1114     29,
1115     NULL,
1116     NULL,
1117     NULL
1118   },
1119   {
1120     EL_SP_EXIT_CLOSING,
1121     EL_SP_EXIT_CLOSED,
1122     29,
1123     NULL,
1124     NULL,
1125     NULL
1126   },
1127   {
1128     EL_SWITCHGATE_OPENING,
1129     EL_SWITCHGATE_OPEN,
1130     29,
1131     NULL,
1132     NULL,
1133     NULL
1134   },
1135   {
1136     EL_SWITCHGATE_CLOSING,
1137     EL_SWITCHGATE_CLOSED,
1138     29,
1139     NULL,
1140     NULL,
1141     NULL
1142   },
1143   {
1144     EL_TIMEGATE_OPENING,
1145     EL_TIMEGATE_OPEN,
1146     29,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151   {
1152     EL_TIMEGATE_CLOSING,
1153     EL_TIMEGATE_CLOSED,
1154     29,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159
1160   {
1161     EL_ACID_SPLASH_LEFT,
1162     EL_EMPTY,
1163     8,
1164     NULL,
1165     NULL,
1166     NULL
1167   },
1168   {
1169     EL_ACID_SPLASH_RIGHT,
1170     EL_EMPTY,
1171     8,
1172     NULL,
1173     NULL,
1174     NULL
1175   },
1176   {
1177     EL_SP_BUGGY_BASE,
1178     EL_SP_BUGGY_BASE_ACTIVATING,
1179     0,
1180     InitBuggyBase,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_SP_BUGGY_BASE_ACTIVATING,
1186     EL_SP_BUGGY_BASE_ACTIVE,
1187     0,
1188     InitBuggyBase,
1189     NULL,
1190     NULL
1191   },
1192   {
1193     EL_SP_BUGGY_BASE_ACTIVE,
1194     EL_SP_BUGGY_BASE,
1195     0,
1196     InitBuggyBase,
1197     WarnBuggyBase,
1198     NULL
1199   },
1200   {
1201     EL_TRAP,
1202     EL_TRAP_ACTIVE,
1203     0,
1204     InitTrap,
1205     NULL,
1206     ActivateTrap
1207   },
1208   {
1209     EL_TRAP_ACTIVE,
1210     EL_TRAP,
1211     31,
1212     NULL,
1213     ChangeActiveTrap,
1214     NULL
1215   },
1216   {
1217     EL_ROBOT_WHEEL_ACTIVE,
1218     EL_ROBOT_WHEEL,
1219     0,
1220     InitRobotWheel,
1221     RunRobotWheel,
1222     StopRobotWheel
1223   },
1224   {
1225     EL_TIMEGATE_SWITCH_ACTIVE,
1226     EL_TIMEGATE_SWITCH,
1227     0,
1228     InitTimegateWheel,
1229     RunTimegateWheel,
1230     NULL
1231   },
1232   {
1233     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1234     EL_DC_TIMEGATE_SWITCH,
1235     0,
1236     InitTimegateWheel,
1237     RunTimegateWheel,
1238     NULL
1239   },
1240   {
1241     EL_EMC_MAGIC_BALL_ACTIVE,
1242     EL_EMC_MAGIC_BALL_ACTIVE,
1243     0,
1244     InitMagicBallDelay,
1245     NULL,
1246     ActivateMagicBall
1247   },
1248   {
1249     EL_EMC_SPRING_BUMPER_ACTIVE,
1250     EL_EMC_SPRING_BUMPER,
1251     8,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_DIAGONAL_SHRINKING,
1258     EL_UNDEFINED,
1259     0,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_DIAGONAL_GROWING,
1266     EL_UNDEFINED,
1267     0,
1268     NULL,
1269     NULL,
1270     NULL,
1271   },
1272
1273   {
1274     EL_UNDEFINED,
1275     EL_UNDEFINED,
1276     -1,
1277     NULL,
1278     NULL,
1279     NULL
1280   }
1281 };
1282
1283 struct
1284 {
1285   int element;
1286   int push_delay_fixed, push_delay_random;
1287 }
1288 push_delay_list[] =
1289 {
1290   { EL_SPRING,                  0, 0 },
1291   { EL_BALLOON,                 0, 0 },
1292
1293   { EL_SOKOBAN_OBJECT,          2, 0 },
1294   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1295   { EL_SATELLITE,               2, 0 },
1296   { EL_SP_DISK_YELLOW,          2, 0 },
1297
1298   { EL_UNDEFINED,               0, 0 },
1299 };
1300
1301 struct
1302 {
1303   int element;
1304   int move_stepsize;
1305 }
1306 move_stepsize_list[] =
1307 {
1308   { EL_AMOEBA_DROP,             2 },
1309   { EL_AMOEBA_DROPPING,         2 },
1310   { EL_QUICKSAND_FILLING,       1 },
1311   { EL_QUICKSAND_EMPTYING,      1 },
1312   { EL_QUICKSAND_FAST_FILLING,  2 },
1313   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1314   { EL_MAGIC_WALL_FILLING,      2 },
1315   { EL_MAGIC_WALL_EMPTYING,     2 },
1316   { EL_BD_MAGIC_WALL_FILLING,   2 },
1317   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1318   { EL_DC_MAGIC_WALL_FILLING,   2 },
1319   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1320
1321   { EL_UNDEFINED,               0 },
1322 };
1323
1324 struct
1325 {
1326   int element;
1327   int count;
1328 }
1329 collect_count_list[] =
1330 {
1331   { EL_EMERALD,                 1 },
1332   { EL_BD_DIAMOND,              1 },
1333   { EL_EMERALD_YELLOW,          1 },
1334   { EL_EMERALD_RED,             1 },
1335   { EL_EMERALD_PURPLE,          1 },
1336   { EL_DIAMOND,                 3 },
1337   { EL_SP_INFOTRON,             1 },
1338   { EL_PEARL,                   5 },
1339   { EL_CRYSTAL,                 8 },
1340
1341   { EL_UNDEFINED,               0 },
1342 };
1343
1344 struct
1345 {
1346   int element;
1347   int direction;
1348 }
1349 access_direction_list[] =
1350 {
1351   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1352   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1353   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1354   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1355   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1356   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1357   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1358   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1359   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1360   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1361   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1362
1363   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1364   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1365   { EL_SP_PORT_UP,                                                   MV_DOWN },
1366   { EL_SP_PORT_DOWN,                                         MV_UP           },
1367   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1368   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1369   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1370   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1371   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1372   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1373   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1374   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1375   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1376   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1377   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1378   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1379   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1380   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1381   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1382
1383   { EL_UNDEFINED,                       MV_NONE                              }
1384 };
1385
1386 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1387
1388 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1389 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1390 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1391                                  IS_JUST_CHANGING(x, y))
1392
1393 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1394
1395 /* static variables for playfield scan mode (scanning forward or backward) */
1396 static int playfield_scan_start_x = 0;
1397 static int playfield_scan_start_y = 0;
1398 static int playfield_scan_delta_x = 1;
1399 static int playfield_scan_delta_y = 1;
1400
1401 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1402                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1403                                      (y) += playfield_scan_delta_y)     \
1404                                 for ((x) = playfield_scan_start_x;      \
1405                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1406                                      (x) += playfield_scan_delta_x)
1407
1408 #ifdef DEBUG
1409 void DEBUG_SetMaximumDynamite()
1410 {
1411   int i;
1412
1413   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1414     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1415       local_player->inventory_element[local_player->inventory_size++] =
1416         EL_DYNAMITE;
1417 }
1418 #endif
1419
1420 static void InitPlayfieldScanModeVars()
1421 {
1422   if (game.use_reverse_scan_direction)
1423   {
1424     playfield_scan_start_x = lev_fieldx - 1;
1425     playfield_scan_start_y = lev_fieldy - 1;
1426
1427     playfield_scan_delta_x = -1;
1428     playfield_scan_delta_y = -1;
1429   }
1430   else
1431   {
1432     playfield_scan_start_x = 0;
1433     playfield_scan_start_y = 0;
1434
1435     playfield_scan_delta_x = 1;
1436     playfield_scan_delta_y = 1;
1437   }
1438 }
1439
1440 static void InitPlayfieldScanMode(int mode)
1441 {
1442   game.use_reverse_scan_direction =
1443     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1444
1445   InitPlayfieldScanModeVars();
1446 }
1447
1448 static int get_move_delay_from_stepsize(int move_stepsize)
1449 {
1450   move_stepsize =
1451     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1452
1453   /* make sure that stepsize value is always a power of 2 */
1454   move_stepsize = (1 << log_2(move_stepsize));
1455
1456   return TILEX / move_stepsize;
1457 }
1458
1459 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1460                                boolean init_game)
1461 {
1462   int player_nr = player->index_nr;
1463   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1464   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1465
1466   /* do no immediately change move delay -- the player might just be moving */
1467   player->move_delay_value_next = move_delay;
1468
1469   /* information if player can move must be set separately */
1470   player->cannot_move = cannot_move;
1471
1472   if (init_game)
1473   {
1474     player->move_delay       = game.initial_move_delay[player_nr];
1475     player->move_delay_value = game.initial_move_delay_value[player_nr];
1476
1477     player->move_delay_value_next = -1;
1478
1479     player->move_delay_reset_counter = 0;
1480   }
1481 }
1482
1483 void GetPlayerConfig()
1484 {
1485   GameFrameDelay = setup.game_frame_delay;
1486
1487   if (!audio.sound_available)
1488     setup.sound_simple = FALSE;
1489
1490   if (!audio.loops_available)
1491     setup.sound_loops = FALSE;
1492
1493   if (!audio.music_available)
1494     setup.sound_music = FALSE;
1495
1496   if (!video.fullscreen_available)
1497     setup.fullscreen = FALSE;
1498
1499   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1500
1501   SetAudioMode(setup.sound);
1502   InitJoysticks();
1503 }
1504
1505 int GetElementFromGroupElement(int element)
1506 {
1507   if (IS_GROUP_ELEMENT(element))
1508   {
1509     struct ElementGroupInfo *group = element_info[element].group;
1510     int last_anim_random_frame = gfx.anim_random_frame;
1511     int element_pos;
1512
1513     if (group->choice_mode == ANIM_RANDOM)
1514       gfx.anim_random_frame = RND(group->num_elements_resolved);
1515
1516     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1517                                     group->choice_mode, 0,
1518                                     group->choice_pos);
1519
1520     if (group->choice_mode == ANIM_RANDOM)
1521       gfx.anim_random_frame = last_anim_random_frame;
1522
1523     group->choice_pos++;
1524
1525     element = group->element_resolved[element_pos];
1526   }
1527
1528   return element;
1529 }
1530
1531 static void InitPlayerField(int x, int y, int element, boolean init_game)
1532 {
1533   if (element == EL_SP_MURPHY)
1534   {
1535     if (init_game)
1536     {
1537       if (stored_player[0].present)
1538       {
1539         Feld[x][y] = EL_SP_MURPHY_CLONE;
1540
1541         return;
1542       }
1543       else
1544       {
1545         stored_player[0].use_murphy = TRUE;
1546
1547         if (!level.use_artwork_element[0])
1548           stored_player[0].artwork_element = EL_SP_MURPHY;
1549       }
1550
1551       Feld[x][y] = EL_PLAYER_1;
1552     }
1553   }
1554
1555   if (init_game)
1556   {
1557     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1558     int jx = player->jx, jy = player->jy;
1559
1560     player->present = TRUE;
1561
1562     player->block_last_field = (element == EL_SP_MURPHY ?
1563                                 level.sp_block_last_field :
1564                                 level.block_last_field);
1565
1566     /* ---------- initialize player's last field block delay --------------- */
1567
1568     /* always start with reliable default value (no adjustment needed) */
1569     player->block_delay_adjustment = 0;
1570
1571     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1572     if (player->block_last_field && element == EL_SP_MURPHY)
1573       player->block_delay_adjustment = 1;
1574
1575     /* special case 2: in game engines before 3.1.1, blocking was different */
1576     if (game.use_block_last_field_bug)
1577       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1578
1579     if (!options.network || player->connected)
1580     {
1581       player->active = TRUE;
1582
1583       /* remove potentially duplicate players */
1584       if (StorePlayer[jx][jy] == Feld[x][y])
1585         StorePlayer[jx][jy] = 0;
1586
1587       StorePlayer[x][y] = Feld[x][y];
1588
1589       if (options.debug)
1590       {
1591         printf("Player %d activated.\n", player->element_nr);
1592         printf("[Local player is %d and currently %s.]\n",
1593                local_player->element_nr,
1594                local_player->active ? "active" : "not active");
1595       }
1596     }
1597
1598     Feld[x][y] = EL_EMPTY;
1599
1600     player->jx = player->last_jx = x;
1601     player->jy = player->last_jy = y;
1602   }
1603 }
1604
1605 static void InitField(int x, int y, boolean init_game)
1606 {
1607   int element = Feld[x][y];
1608
1609   switch (element)
1610   {
1611     case EL_SP_MURPHY:
1612     case EL_PLAYER_1:
1613     case EL_PLAYER_2:
1614     case EL_PLAYER_3:
1615     case EL_PLAYER_4:
1616       InitPlayerField(x, y, element, init_game);
1617       break;
1618
1619     case EL_SOKOBAN_FIELD_PLAYER:
1620       element = Feld[x][y] = EL_PLAYER_1;
1621       InitField(x, y, init_game);
1622
1623       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1624       InitField(x, y, init_game);
1625       break;
1626
1627     case EL_SOKOBAN_FIELD_EMPTY:
1628       local_player->sokobanfields_still_needed++;
1629       break;
1630
1631     case EL_STONEBLOCK:
1632       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1633         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1634       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1635         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1636       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1637         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1638       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1639         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1640       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1641         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1642       break;
1643
1644     case EL_BUG:
1645     case EL_BUG_RIGHT:
1646     case EL_BUG_UP:
1647     case EL_BUG_LEFT:
1648     case EL_BUG_DOWN:
1649     case EL_SPACESHIP:
1650     case EL_SPACESHIP_RIGHT:
1651     case EL_SPACESHIP_UP:
1652     case EL_SPACESHIP_LEFT:
1653     case EL_SPACESHIP_DOWN:
1654     case EL_BD_BUTTERFLY:
1655     case EL_BD_BUTTERFLY_RIGHT:
1656     case EL_BD_BUTTERFLY_UP:
1657     case EL_BD_BUTTERFLY_LEFT:
1658     case EL_BD_BUTTERFLY_DOWN:
1659     case EL_BD_FIREFLY:
1660     case EL_BD_FIREFLY_RIGHT:
1661     case EL_BD_FIREFLY_UP:
1662     case EL_BD_FIREFLY_LEFT:
1663     case EL_BD_FIREFLY_DOWN:
1664     case EL_PACMAN_RIGHT:
1665     case EL_PACMAN_UP:
1666     case EL_PACMAN_LEFT:
1667     case EL_PACMAN_DOWN:
1668     case EL_YAMYAM:
1669     case EL_YAMYAM_LEFT:
1670     case EL_YAMYAM_RIGHT:
1671     case EL_YAMYAM_UP:
1672     case EL_YAMYAM_DOWN:
1673     case EL_DARK_YAMYAM:
1674     case EL_ROBOT:
1675     case EL_PACMAN:
1676     case EL_SP_SNIKSNAK:
1677     case EL_SP_ELECTRON:
1678     case EL_MOLE:
1679     case EL_MOLE_LEFT:
1680     case EL_MOLE_RIGHT:
1681     case EL_MOLE_UP:
1682     case EL_MOLE_DOWN:
1683       InitMovDir(x, y);
1684       break;
1685
1686     case EL_AMOEBA_FULL:
1687     case EL_BD_AMOEBA:
1688       InitAmoebaNr(x, y);
1689       break;
1690
1691     case EL_AMOEBA_DROP:
1692       if (y == lev_fieldy - 1)
1693       {
1694         Feld[x][y] = EL_AMOEBA_GROWING;
1695         Store[x][y] = EL_AMOEBA_WET;
1696       }
1697       break;
1698
1699     case EL_DYNAMITE_ACTIVE:
1700     case EL_SP_DISK_RED_ACTIVE:
1701     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1702     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1703     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1704     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1705       MovDelay[x][y] = 96;
1706       break;
1707
1708     case EL_EM_DYNAMITE_ACTIVE:
1709       MovDelay[x][y] = 32;
1710       break;
1711
1712     case EL_LAMP:
1713       local_player->lights_still_needed++;
1714       break;
1715
1716     case EL_PENGUIN:
1717       local_player->friends_still_needed++;
1718       break;
1719
1720     case EL_PIG:
1721     case EL_DRAGON:
1722       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1723       break;
1724
1725     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1726     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1727     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1728     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1729     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1730     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1731     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1732     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1733     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1734     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1735     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1736     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1737       if (init_game)
1738       {
1739         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1740         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1741         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1742
1743         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1744         {
1745           game.belt_dir[belt_nr] = belt_dir;
1746           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1747         }
1748         else    /* more than one switch -- set it like the first switch */
1749         {
1750           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1751         }
1752       }
1753       break;
1754
1755 #if !USE_BOTH_SWITCHGATE_SWITCHES
1756     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1757       if (init_game)
1758         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1759       break;
1760
1761     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1762       if (init_game)
1763         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1764       break;
1765 #endif
1766
1767     case EL_LIGHT_SWITCH_ACTIVE:
1768       if (init_game)
1769         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1770       break;
1771
1772     case EL_INVISIBLE_STEELWALL:
1773     case EL_INVISIBLE_WALL:
1774     case EL_INVISIBLE_SAND:
1775       if (game.light_time_left > 0 ||
1776           game.lenses_time_left > 0)
1777         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1778       break;
1779
1780     case EL_EMC_MAGIC_BALL:
1781       if (game.ball_state)
1782         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1783       break;
1784
1785     case EL_EMC_MAGIC_BALL_SWITCH:
1786       if (game.ball_state)
1787         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1788       break;
1789
1790     default:
1791       if (IS_CUSTOM_ELEMENT(element))
1792       {
1793         if (CAN_MOVE(element))
1794           InitMovDir(x, y);
1795
1796 #if USE_NEW_CUSTOM_VALUE
1797         if (!element_info[element].use_last_ce_value || init_game)
1798           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1799 #endif
1800       }
1801       else if (IS_GROUP_ELEMENT(element))
1802       {
1803         Feld[x][y] = GetElementFromGroupElement(element);
1804
1805         InitField(x, y, init_game);
1806       }
1807
1808       break;
1809   }
1810
1811   if (!init_game)
1812     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1813 }
1814
1815 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1816 {
1817   InitField(x, y, init_game);
1818
1819   /* not needed to call InitMovDir() -- already done by InitField()! */
1820   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1821       CAN_MOVE(Feld[x][y]))
1822     InitMovDir(x, y);
1823 }
1824
1825 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1826 {
1827   int old_element = Feld[x][y];
1828
1829   InitField(x, y, init_game);
1830
1831   /* not needed to call InitMovDir() -- already done by InitField()! */
1832   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1833       CAN_MOVE(old_element) &&
1834       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1835     InitMovDir(x, y);
1836
1837   /* this case is in fact a combination of not less than three bugs:
1838      first, it calls InitMovDir() for elements that can move, although this is
1839      already done by InitField(); then, it checks the element that was at this
1840      field _before_ the call to InitField() (which can change it); lastly, it
1841      was not called for "mole with direction" elements, which were treated as
1842      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1843   */
1844 }
1845
1846 #if 1
1847
1848 static int get_key_element_from_nr(int key_nr)
1849 {
1850   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1851                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1852                           EL_EM_KEY_1 : EL_KEY_1);
1853
1854   return key_base_element + key_nr;
1855 }
1856
1857 static int get_next_dropped_element(struct PlayerInfo *player)
1858 {
1859   return (player->inventory_size > 0 ?
1860           player->inventory_element[player->inventory_size - 1] :
1861           player->inventory_infinite_element != EL_UNDEFINED ?
1862           player->inventory_infinite_element :
1863           player->dynabombs_left > 0 ?
1864           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1865           EL_UNDEFINED);
1866 }
1867
1868 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1869 {
1870   /* pos >= 0: get element from bottom of the stack;
1871      pos <  0: get element from top of the stack */
1872
1873   if (pos < 0)
1874   {
1875     int min_inventory_size = -pos;
1876     int inventory_pos = player->inventory_size - min_inventory_size;
1877     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1878
1879     return (player->inventory_size >= min_inventory_size ?
1880             player->inventory_element[inventory_pos] :
1881             player->inventory_infinite_element != EL_UNDEFINED ?
1882             player->inventory_infinite_element :
1883             player->dynabombs_left >= min_dynabombs_left ?
1884             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1885             EL_UNDEFINED);
1886   }
1887   else
1888   {
1889     int min_dynabombs_left = pos + 1;
1890     int min_inventory_size = pos + 1 - player->dynabombs_left;
1891     int inventory_pos = pos - player->dynabombs_left;
1892
1893     return (player->inventory_infinite_element != EL_UNDEFINED ?
1894             player->inventory_infinite_element :
1895             player->dynabombs_left >= min_dynabombs_left ?
1896             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1897             player->inventory_size >= min_inventory_size ?
1898             player->inventory_element[inventory_pos] :
1899             EL_UNDEFINED);
1900   }
1901 }
1902
1903 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
1904 {
1905   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
1906   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
1907   int compare_result;
1908
1909   if (gpo1->sort_priority != gpo2->sort_priority)
1910     compare_result = gpo1->sort_priority - gpo2->sort_priority;
1911   else
1912     compare_result = gpo1->nr - gpo2->nr;
1913
1914   return compare_result;
1915 }
1916
1917 void InitGameControlValues()
1918 {
1919   int i;
1920
1921   for (i = 0; game_panel_controls[i].nr != -1; i++)
1922   {
1923     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
1924     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
1925     struct TextPosInfo *pos = gpc->pos;
1926     int nr = gpc->nr;
1927     int type = gpc->type;
1928
1929     if (nr != i)
1930     {
1931       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
1932       Error(ERR_EXIT, "this should not happen -- please debug");
1933     }
1934
1935     /* force update of game controls after initialization */
1936     gpc->value = gpc->last_value = -1;
1937     gpc->frame = gpc->last_frame = -1;
1938     gpc->gfx_frame = -1;
1939
1940     /* determine panel value width for later calculation of alignment */
1941     if (type == TYPE_INTEGER || type == TYPE_STRING)
1942     {
1943       pos->width = pos->size * getFontWidth(pos->font);
1944       pos->height = getFontHeight(pos->font);
1945     }
1946     else if (type == TYPE_ELEMENT)
1947     {
1948       pos->width = pos->size;
1949       pos->height = pos->size;
1950     }
1951
1952     /* fill structure for game panel draw order */
1953     gpo->nr = gpc->nr;
1954     gpo->sort_priority = pos->sort_priority;
1955   }
1956
1957   /* sort game panel controls according to sort_priority and control number */
1958   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
1959         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
1960 }
1961
1962 void UpdateGameControlValues()
1963 {
1964   int i, k;
1965   int time = (level.time == 0 ? TimePlayed : TimeLeft);
1966   int score = (local_player->LevelSolved ? local_player->score_final :
1967                local_player->score);
1968   int exit_closed = (local_player->gems_still_needed > 0 ||
1969                      local_player->sokobanfields_still_needed > 0 ||
1970                      local_player->lights_still_needed > 0);
1971
1972   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
1973   game_panel_controls[GAME_PANEL_GEMS].value =
1974     local_player->gems_still_needed;
1975
1976   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
1977   for (i = 0; i < MAX_NUM_KEYS; i++)
1978     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
1979   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
1980   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
1981
1982   if (game.centered_player_nr == -1)
1983   {
1984     for (i = 0; i < MAX_PLAYERS; i++)
1985     {
1986       for (k = 0; k < MAX_NUM_KEYS; k++)
1987         if (stored_player[i].key[k])
1988           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1989             get_key_element_from_nr(k);
1990
1991       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1992         stored_player[i].inventory_size;
1993
1994       if (stored_player[i].num_white_keys > 0)
1995         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
1996           EL_DC_KEY_WHITE;
1997
1998       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1999         stored_player[i].num_white_keys;
2000     }
2001   }
2002   else
2003   {
2004     int player_nr = game.centered_player_nr;
2005
2006     for (k = 0; k < MAX_NUM_KEYS; k++)
2007       if (stored_player[player_nr].key[k])
2008         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2009           get_key_element_from_nr(k);
2010
2011     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2012       stored_player[player_nr].inventory_size;
2013
2014     if (stored_player[player_nr].num_white_keys > 0)
2015       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2016
2017     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2018       stored_player[player_nr].num_white_keys;
2019   }
2020
2021   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2022   {
2023     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2024       get_inventory_element_from_pos(local_player, i);
2025     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2026       get_inventory_element_from_pos(local_player, -i - 1);
2027   }
2028
2029   game_panel_controls[GAME_PANEL_SCORE].value = score;
2030
2031   game_panel_controls[GAME_PANEL_TIME].value = time;
2032
2033   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2034   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2035   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2036
2037   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2038     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2039      EL_EMPTY);
2040   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2041     local_player->shield_normal_time_left;
2042   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2043     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2044      EL_EMPTY);
2045   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2046     local_player->shield_deadly_time_left;
2047
2048   game_panel_controls[GAME_PANEL_EXIT].value =
2049     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2050
2051   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2052     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2053   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2054     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2055      EL_EMC_MAGIC_BALL_SWITCH);
2056
2057   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2058     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2059   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2060     game.light_time_left;
2061
2062   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2063     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2064   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2065     game.timegate_time_left;
2066
2067   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2068     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2069
2070   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2071     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2072   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2073     game.lenses_time_left;
2074
2075   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2076     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2077   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2078     game.magnify_time_left;
2079
2080   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2081     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2082      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2083      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2084      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2085      EL_BALLOON_SWITCH_NONE);
2086
2087   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2088     local_player->dynabomb_count;
2089   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2090     local_player->dynabomb_size;
2091   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2092     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2093
2094   game_panel_controls[GAME_PANEL_PENGUINS].value =
2095     local_player->friends_still_needed;
2096
2097   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2098     local_player->sokobanfields_still_needed;
2099   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2100     local_player->sokobanfields_still_needed;
2101
2102   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2103     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2104
2105   for (i = 0; i < NUM_BELTS; i++)
2106   {
2107     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2108       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2109        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2110     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2111       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2112   }
2113
2114   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2115     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2116   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2117     game.magic_wall_time_left;
2118
2119 #if USE_PLAYER_GRAVITY
2120   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2121     local_player->gravity;
2122 #else
2123   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2124 #endif
2125
2126   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2127   {
2128     if (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id))
2129     {
2130       int ce_score = element_info[game.panel.ce_score[i].id].collect_score;
2131
2132       game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = ce_score;
2133       game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = ce_score;
2134     }
2135     else
2136     {
2137       game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = 0;
2138       game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = 0;
2139     }
2140   }
2141
2142   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2143   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2144   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2145
2146   for (i = 0; game_panel_controls[i].nr != -1; i++)
2147   {
2148     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2149
2150     if (gpc->type == TYPE_ELEMENT)
2151     {
2152       if (gpc->value != gpc->last_value)
2153         gpc->gfx_frame = 0;
2154       else
2155         gpc->gfx_frame++;
2156
2157       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2158                                             gpc->gfx_frame);
2159     }
2160   }
2161 }
2162
2163 void DisplayGameControlValues()
2164 {
2165   boolean redraw_panel = FALSE;
2166   int i;
2167
2168   for (i = 0; game_panel_controls[i].nr != -1; i++)
2169   {
2170     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2171
2172     if (PANEL_DEACTIVATED(gpc->pos))
2173       continue;
2174
2175     if (gpc->value == gpc->last_value &&
2176         gpc->frame == gpc->last_frame)
2177       continue;
2178
2179     redraw_panel = TRUE;
2180   }
2181
2182   if (!redraw_panel)
2183     return;
2184
2185   /* copy default game door content to main double buffer */
2186   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2187              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2188
2189   /* redraw game control buttons */
2190 #if 1
2191   RedrawGameButtons();
2192 #else
2193   UnmapGameButtons();
2194   MapGameButtons();
2195 #endif
2196
2197   game_status = GAME_MODE_PSEUDO_PANEL;
2198
2199 #if 1
2200   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2201 #else
2202   for (i = 0; game_panel_controls[i].nr != -1; i++)
2203 #endif
2204   {
2205 #if 1
2206     int nr = game_panel_order[i].nr;
2207     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2208 #else
2209     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2210     int nr = gpc->nr;
2211 #endif
2212     struct TextPosInfo *pos = gpc->pos;
2213     int type = gpc->type;
2214     int value = gpc->value;
2215     int frame = gpc->frame;
2216 #if 0
2217     int last_value = gpc->last_value;
2218     int last_frame = gpc->last_frame;
2219 #endif
2220     int size = pos->size;
2221     int font = pos->font;
2222     boolean draw_masked = pos->draw_masked;
2223     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2224
2225     if (PANEL_DEACTIVATED(pos))
2226       continue;
2227
2228 #if 0
2229     if (value == last_value && frame == last_frame)
2230       continue;
2231 #endif
2232
2233     gpc->last_value = value;
2234     gpc->last_frame = frame;
2235
2236 #if 0
2237     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2238 #endif
2239
2240     if (type == TYPE_INTEGER)
2241     {
2242       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2243           nr == GAME_PANEL_TIME)
2244       {
2245         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2246
2247         if (use_dynamic_size)           /* use dynamic number of digits */
2248         {
2249           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2250           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2251           int size2 = size1 + 1;
2252           int font1 = pos->font;
2253           int font2 = pos->font_alt;
2254
2255           size = (value < value_change ? size1 : size2);
2256           font = (value < value_change ? font1 : font2);
2257
2258 #if 0
2259           /* clear background if value just changed its size (dynamic digits) */
2260           if ((last_value < value_change) != (value < value_change))
2261           {
2262             int width1 = size1 * getFontWidth(font1);
2263             int width2 = size2 * getFontWidth(font2);
2264             int max_width = MAX(width1, width2);
2265             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2266
2267             pos->width = max_width;
2268
2269             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2270                                        max_width, max_height);
2271           }
2272 #endif
2273         }
2274       }
2275
2276 #if 1
2277       /* correct text size if "digits" is zero or less */
2278       if (size <= 0)
2279         size = strlen(int2str(value, size));
2280
2281       /* dynamically correct text alignment */
2282       pos->width = size * getFontWidth(font);
2283 #endif
2284
2285       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2286                   int2str(value, size), font, mask_mode);
2287     }
2288     else if (type == TYPE_ELEMENT)
2289     {
2290       int element, graphic;
2291       Bitmap *src_bitmap;
2292       int src_x, src_y;
2293       int width, height;
2294       int dst_x = PANEL_XPOS(pos);
2295       int dst_y = PANEL_YPOS(pos);
2296
2297 #if 1
2298       if (value != EL_UNDEFINED && value != EL_EMPTY)
2299       {
2300         element = value;
2301         graphic = el2panelimg(value);
2302
2303         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2304                               &src_x, &src_y);
2305
2306         width  = graphic_info[graphic].width  * size / TILESIZE;
2307         height = graphic_info[graphic].height * size / TILESIZE;
2308
2309         if (draw_masked)
2310         {
2311           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2312                         dst_x - src_x, dst_y - src_y);
2313           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2314                            dst_x, dst_y);
2315         }
2316         else
2317         {
2318           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2319                      dst_x, dst_y);
2320         }
2321       }
2322 #else
2323       if (value == EL_UNDEFINED || value == EL_EMPTY)
2324       {
2325         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2326         graphic = el2panelimg(element);
2327
2328         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2329         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2330         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2331       }
2332       else
2333       {
2334         element = value;
2335         graphic = el2panelimg(value);
2336
2337         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2338       }
2339
2340       width  = graphic_info[graphic].width  * size / TILESIZE;
2341       height = graphic_info[graphic].height * size / TILESIZE;
2342
2343       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2344 #endif
2345     }
2346     else if (type == TYPE_STRING)
2347     {
2348       boolean active = (value != 0);
2349       char *state_normal = "off";
2350       char *state_active = "on";
2351       char *state = (active ? state_active : state_normal);
2352       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2353                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2354                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2355                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2356
2357       if (nr == GAME_PANEL_GRAVITY_STATE)
2358       {
2359         int font1 = pos->font;          /* (used for normal state) */
2360         int font2 = pos->font_alt;      /* (used for active state) */
2361 #if 0
2362         int size1 = strlen(state_normal);
2363         int size2 = strlen(state_active);
2364         int width1 = size1 * getFontWidth(font1);
2365         int width2 = size2 * getFontWidth(font2);
2366         int max_width = MAX(width1, width2);
2367         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2368
2369         pos->width = max_width;
2370
2371         /* clear background for values that may have changed its size */
2372         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2373                                    max_width, max_height);
2374 #endif
2375
2376         font = (active ? font2 : font1);
2377       }
2378
2379       if (s != NULL)
2380       {
2381         char *s_cut;
2382
2383 #if 1
2384         if (size <= 0)
2385         {
2386           /* don't truncate output if "chars" is zero or less */
2387           size = strlen(s);
2388
2389           /* dynamically correct text alignment */
2390           pos->width = size * getFontWidth(font);
2391         }
2392 #endif
2393
2394         s_cut = getStringCopyN(s, size);
2395
2396         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2397                     s_cut, font, mask_mode);
2398
2399         free(s_cut);
2400       }
2401     }
2402
2403     redraw_mask |= REDRAW_DOOR_1;
2404   }
2405
2406   game_status = GAME_MODE_PLAYING;
2407 }
2408
2409 void DrawGameValue_Emeralds(int value)
2410 {
2411   struct TextPosInfo *pos = &game.panel.gems;
2412 #if 1
2413   int font_nr = pos->font;
2414 #else
2415   int font_nr = FONT_TEXT_2;
2416 #endif
2417   int font_width = getFontWidth(font_nr);
2418   int chars = pos->size;
2419
2420 #if 1
2421   return;       /* !!! USE NEW STUFF !!! */
2422 #endif
2423
2424   if (PANEL_DEACTIVATED(pos))
2425     return;
2426
2427   pos->width = chars * font_width;
2428
2429   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2430 }
2431
2432 void DrawGameValue_Dynamite(int value)
2433 {
2434   struct TextPosInfo *pos = &game.panel.inventory_count;
2435 #if 1
2436   int font_nr = pos->font;
2437 #else
2438   int font_nr = FONT_TEXT_2;
2439 #endif
2440   int font_width = getFontWidth(font_nr);
2441   int chars = pos->size;
2442
2443 #if 1
2444   return;       /* !!! USE NEW STUFF !!! */
2445 #endif
2446
2447   if (PANEL_DEACTIVATED(pos))
2448     return;
2449
2450   pos->width = chars * font_width;
2451
2452   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2453 }
2454
2455 void DrawGameValue_Score(int value)
2456 {
2457   struct TextPosInfo *pos = &game.panel.score;
2458 #if 1
2459   int font_nr = pos->font;
2460 #else
2461   int font_nr = FONT_TEXT_2;
2462 #endif
2463   int font_width = getFontWidth(font_nr);
2464   int chars = pos->size;
2465
2466 #if 1
2467   return;       /* !!! USE NEW STUFF !!! */
2468 #endif
2469
2470   if (PANEL_DEACTIVATED(pos))
2471     return;
2472
2473   pos->width = chars * font_width;
2474
2475   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2476 }
2477
2478 void DrawGameValue_Time(int value)
2479 {
2480   struct TextPosInfo *pos = &game.panel.time;
2481   static int last_value = -1;
2482   int chars1 = 3;
2483   int chars2 = 4;
2484   int chars = pos->size;
2485 #if 1
2486   int font1_nr = pos->font;
2487   int font2_nr = pos->font_alt;
2488 #else
2489   int font1_nr = FONT_TEXT_2;
2490   int font2_nr = FONT_TEXT_1;
2491 #endif
2492   int font_nr = font1_nr;
2493   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2494
2495 #if 1
2496   return;       /* !!! USE NEW STUFF !!! */
2497 #endif
2498
2499   if (PANEL_DEACTIVATED(pos))
2500     return;
2501
2502   if (use_dynamic_chars)                /* use dynamic number of chars */
2503   {
2504     chars   = (value < 1000 ? chars1   : chars2);
2505     font_nr = (value < 1000 ? font1_nr : font2_nr);
2506   }
2507
2508   /* clear background if value just changed its size (dynamic chars only) */
2509   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2510   {
2511     int width1 = chars1 * getFontWidth(font1_nr);
2512     int width2 = chars2 * getFontWidth(font2_nr);
2513     int max_width = MAX(width1, width2);
2514     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2515
2516     pos->width = max_width;
2517
2518     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2519                                max_width, max_height);
2520   }
2521
2522   pos->width = chars * getFontWidth(font_nr);
2523
2524   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2525
2526   last_value = value;
2527 }
2528
2529 void DrawGameValue_Level(int value)
2530 {
2531   struct TextPosInfo *pos = &game.panel.level_number;
2532   int chars1 = 2;
2533   int chars2 = 3;
2534   int chars = pos->size;
2535 #if 1
2536   int font1_nr = pos->font;
2537   int font2_nr = pos->font_alt;
2538 #else
2539   int font1_nr = FONT_TEXT_2;
2540   int font2_nr = FONT_TEXT_1;
2541 #endif
2542   int font_nr = font1_nr;
2543   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2544
2545 #if 1
2546   return;       /* !!! USE NEW STUFF !!! */
2547 #endif
2548
2549   if (PANEL_DEACTIVATED(pos))
2550     return;
2551
2552   if (use_dynamic_chars)                /* use dynamic number of chars */
2553   {
2554     chars   = (level_nr < 100 ? chars1   : chars2);
2555     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2556   }
2557
2558   pos->width = chars * getFontWidth(font_nr);
2559
2560   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2561 }
2562
2563 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2564 {
2565 #if 0
2566   struct TextPosInfo *pos = &game.panel.keys;
2567 #endif
2568 #if 0
2569   int base_key_graphic = EL_KEY_1;
2570 #endif
2571   int i;
2572
2573 #if 1
2574   return;       /* !!! USE NEW STUFF !!! */
2575 #endif
2576
2577 #if 0
2578   if (PANEL_DEACTIVATED(pos))
2579     return;
2580 #endif
2581
2582 #if 0
2583   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2584     base_key_graphic = EL_EM_KEY_1;
2585 #endif
2586
2587 #if 0
2588   pos->width = 4 * MINI_TILEX;
2589 #endif
2590
2591 #if 1
2592   for (i = 0; i < MAX_NUM_KEYS; i++)
2593 #else
2594   /* currently only 4 of 8 possible keys are displayed */
2595   for (i = 0; i < STD_NUM_KEYS; i++)
2596 #endif
2597   {
2598 #if 1
2599     struct TextPosInfo *pos = &game.panel.key[i];
2600 #endif
2601     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2602     int src_y = DOOR_GFX_PAGEY1 + 123;
2603 #if 1
2604     int dst_x = PANEL_XPOS(pos);
2605     int dst_y = PANEL_YPOS(pos);
2606 #else
2607     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2608     int dst_y = PANEL_YPOS(pos);
2609 #endif
2610
2611 #if 1
2612     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2613                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2614                    EL_KEY_1) + i;
2615     int graphic = el2edimg(element);
2616 #endif
2617
2618 #if 1
2619     if (PANEL_DEACTIVATED(pos))
2620       continue;
2621 #endif
2622
2623 #if 0
2624     /* masked blit with tiles from half-size scaled bitmap does not work yet
2625        (no mask bitmap created for these sizes after loading and scaling) --
2626        solution: load without creating mask, scale, then create final mask */
2627
2628     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2629                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2630
2631     if (key[i])
2632     {
2633 #if 0
2634       int graphic = el2edimg(base_key_graphic + i);
2635 #endif
2636       Bitmap *src_bitmap;
2637       int src_x, src_y;
2638
2639       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2640
2641       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2642                     dst_x - src_x, dst_y - src_y);
2643       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2644                        dst_x, dst_y);
2645     }
2646 #else
2647 #if 1
2648     if (key[i])
2649       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2650     else
2651       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2652                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2653 #else
2654     if (key[i])
2655       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2656     else
2657       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2658                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2659 #endif
2660 #endif
2661   }
2662 }
2663
2664 #else
2665
2666 void DrawGameValue_Emeralds(int value)
2667 {
2668   int font_nr = FONT_TEXT_2;
2669   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2670
2671   if (PANEL_DEACTIVATED(game.panel.gems))
2672     return;
2673
2674   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2675 }
2676
2677 void DrawGameValue_Dynamite(int value)
2678 {
2679   int font_nr = FONT_TEXT_2;
2680   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2681
2682   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2683     return;
2684
2685   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2686 }
2687
2688 void DrawGameValue_Score(int value)
2689 {
2690   int font_nr = FONT_TEXT_2;
2691   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2692
2693   if (PANEL_DEACTIVATED(game.panel.score))
2694     return;
2695
2696   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2697 }
2698
2699 void DrawGameValue_Time(int value)
2700 {
2701   int font1_nr = FONT_TEXT_2;
2702 #if 1
2703   int font2_nr = FONT_TEXT_1;
2704 #else
2705   int font2_nr = FONT_LEVEL_NUMBER;
2706 #endif
2707   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2708   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2709
2710   if (PANEL_DEACTIVATED(game.panel.time))
2711     return;
2712
2713   /* clear background if value just changed its size */
2714   if (value == 999 || value == 1000)
2715     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2716
2717   if (value < 1000)
2718     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2719   else
2720     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2721 }
2722
2723 void DrawGameValue_Level(int value)
2724 {
2725   int font1_nr = FONT_TEXT_2;
2726 #if 1
2727   int font2_nr = FONT_TEXT_1;
2728 #else
2729   int font2_nr = FONT_LEVEL_NUMBER;
2730 #endif
2731
2732   if (PANEL_DEACTIVATED(game.panel.level))
2733     return;
2734
2735   if (level_nr < 100)
2736     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2737   else
2738     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2739 }
2740
2741 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2742 {
2743   int base_key_graphic = EL_KEY_1;
2744   int i;
2745
2746   if (PANEL_DEACTIVATED(game.panel.keys))
2747     return;
2748
2749   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2750     base_key_graphic = EL_EM_KEY_1;
2751
2752   /* currently only 4 of 8 possible keys are displayed */
2753   for (i = 0; i < STD_NUM_KEYS; i++)
2754   {
2755     int x = XX_KEYS + i * MINI_TILEX;
2756     int y = YY_KEYS;
2757
2758     if (key[i])
2759       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2760     else
2761       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2762                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2763   }
2764 }
2765
2766 #endif
2767
2768 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2769                        int key_bits)
2770 {
2771   int key[MAX_NUM_KEYS];
2772   int i;
2773
2774   /* prevent EM engine from updating time/score values parallel to GameWon() */
2775   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2776       local_player->LevelSolved)
2777     return;
2778
2779   for (i = 0; i < MAX_NUM_KEYS; i++)
2780     key[i] = key_bits & (1 << i);
2781
2782   DrawGameValue_Level(level_nr);
2783
2784   DrawGameValue_Emeralds(emeralds);
2785   DrawGameValue_Dynamite(dynamite);
2786   DrawGameValue_Score(score);
2787   DrawGameValue_Time(time);
2788
2789   DrawGameValue_Keys(key);
2790 }
2791
2792 void UpdateGameDoorValues()
2793 {
2794   UpdateGameControlValues();
2795 }
2796
2797 void DrawGameDoorValues()
2798 {
2799   DisplayGameControlValues();
2800 }
2801
2802 void DrawGameDoorValues_OLD()
2803 {
2804   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2805   int dynamite_value = 0;
2806   int score_value = (local_player->LevelSolved ? local_player->score_final :
2807                      local_player->score);
2808   int gems_value = local_player->gems_still_needed;
2809   int key_bits = 0;
2810   int i, j;
2811
2812   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2813   {
2814     DrawGameDoorValues_EM();
2815
2816     return;
2817   }
2818
2819   if (game.centered_player_nr == -1)
2820   {
2821     for (i = 0; i < MAX_PLAYERS; i++)
2822     {
2823       for (j = 0; j < MAX_NUM_KEYS; j++)
2824         if (stored_player[i].key[j])
2825           key_bits |= (1 << j);
2826
2827       dynamite_value += stored_player[i].inventory_size;
2828     }
2829   }
2830   else
2831   {
2832     int player_nr = game.centered_player_nr;
2833
2834     for (i = 0; i < MAX_NUM_KEYS; i++)
2835       if (stored_player[player_nr].key[i])
2836         key_bits |= (1 << i);
2837
2838     dynamite_value = stored_player[player_nr].inventory_size;
2839   }
2840
2841   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2842                     key_bits);
2843 }
2844
2845
2846 /*
2847   =============================================================================
2848   InitGameEngine()
2849   -----------------------------------------------------------------------------
2850   initialize game engine due to level / tape version number
2851   =============================================================================
2852 */
2853
2854 static void InitGameEngine()
2855 {
2856   int i, j, k, l, x, y;
2857
2858   /* set game engine from tape file when re-playing, else from level file */
2859   game.engine_version = (tape.playing ? tape.engine_version :
2860                          level.game_version);
2861
2862   /* ---------------------------------------------------------------------- */
2863   /* set flags for bugs and changes according to active game engine version */
2864   /* ---------------------------------------------------------------------- */
2865
2866   /*
2867     Summary of bugfix/change:
2868     Fixed handling for custom elements that change when pushed by the player.
2869
2870     Fixed/changed in version:
2871     3.1.0
2872
2873     Description:
2874     Before 3.1.0, custom elements that "change when pushing" changed directly
2875     after the player started pushing them (until then handled in "DigField()").
2876     Since 3.1.0, these custom elements are not changed until the "pushing"
2877     move of the element is finished (now handled in "ContinueMoving()").
2878
2879     Affected levels/tapes:
2880     The first condition is generally needed for all levels/tapes before version
2881     3.1.0, which might use the old behaviour before it was changed; known tapes
2882     that are affected are some tapes from the level set "Walpurgis Gardens" by
2883     Jamie Cullen.
2884     The second condition is an exception from the above case and is needed for
2885     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2886     above (including some development versions of 3.1.0), but before it was
2887     known that this change would break tapes like the above and was fixed in
2888     3.1.1, so that the changed behaviour was active although the engine version
2889     while recording maybe was before 3.1.0. There is at least one tape that is
2890     affected by this exception, which is the tape for the one-level set "Bug
2891     Machine" by Juergen Bonhagen.
2892   */
2893
2894   game.use_change_when_pushing_bug =
2895     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2896      !(tape.playing &&
2897        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2898        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2899
2900   /*
2901     Summary of bugfix/change:
2902     Fixed handling for blocking the field the player leaves when moving.
2903
2904     Fixed/changed in version:
2905     3.1.1
2906
2907     Description:
2908     Before 3.1.1, when "block last field when moving" was enabled, the field
2909     the player is leaving when moving was blocked for the time of the move,
2910     and was directly unblocked afterwards. This resulted in the last field
2911     being blocked for exactly one less than the number of frames of one player
2912     move. Additionally, even when blocking was disabled, the last field was
2913     blocked for exactly one frame.
2914     Since 3.1.1, due to changes in player movement handling, the last field
2915     is not blocked at all when blocking is disabled. When blocking is enabled,
2916     the last field is blocked for exactly the number of frames of one player
2917     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2918     last field is blocked for exactly one more than the number of frames of
2919     one player move.
2920
2921     Affected levels/tapes:
2922     (!!! yet to be determined -- probably many !!!)
2923   */
2924
2925   game.use_block_last_field_bug =
2926     (game.engine_version < VERSION_IDENT(3,1,1,0));
2927
2928   /*
2929     Summary of bugfix/change:
2930     Changed behaviour of CE changes with multiple changes per single frame.
2931
2932     Fixed/changed in version:
2933     3.2.0-6
2934
2935     Description:
2936     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2937     This resulted in race conditions where CEs seem to behave strange in some
2938     situations (where triggered CE changes were just skipped because there was
2939     already a CE change on that tile in the playfield in that engine frame).
2940     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2941     (The number of changes per frame must be limited in any case, because else
2942     it is easily possible to define CE changes that would result in an infinite
2943     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2944     should be set large enough so that it would only be reached in cases where
2945     the corresponding CE change conditions run into a loop. Therefore, it seems
2946     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2947     maximal number of change pages for custom elements.)
2948
2949     Affected levels/tapes:
2950     Probably many.
2951   */
2952
2953 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2954   game.max_num_changes_per_frame = 1;
2955 #else
2956   game.max_num_changes_per_frame =
2957     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2958 #endif
2959
2960   /* ---------------------------------------------------------------------- */
2961
2962   /* default scan direction: scan playfield from top/left to bottom/right */
2963   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2964
2965   /* dynamically adjust element properties according to game engine version */
2966   InitElementPropertiesEngine(game.engine_version);
2967
2968 #if 0
2969   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2970   printf("          tape version == %06d [%s] [file: %06d]\n",
2971          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2972          tape.file_version);
2973   printf("       => game.engine_version == %06d\n", game.engine_version);
2974 #endif
2975
2976   /* ---------- initialize player's initial move delay --------------------- */
2977
2978   /* dynamically adjust player properties according to level information */
2979   for (i = 0; i < MAX_PLAYERS; i++)
2980     game.initial_move_delay_value[i] =
2981       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2982
2983   /* dynamically adjust player properties according to game engine version */
2984   for (i = 0; i < MAX_PLAYERS; i++)
2985     game.initial_move_delay[i] =
2986       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2987        game.initial_move_delay_value[i] : 0);
2988
2989   /* ---------- initialize player's initial push delay --------------------- */
2990
2991   /* dynamically adjust player properties according to game engine version */
2992   game.initial_push_delay_value =
2993     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2994
2995   /* ---------- initialize changing elements ------------------------------- */
2996
2997   /* initialize changing elements information */
2998   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2999   {
3000     struct ElementInfo *ei = &element_info[i];
3001
3002     /* this pointer might have been changed in the level editor */
3003     ei->change = &ei->change_page[0];
3004
3005     if (!IS_CUSTOM_ELEMENT(i))
3006     {
3007       ei->change->target_element = EL_EMPTY_SPACE;
3008       ei->change->delay_fixed = 0;
3009       ei->change->delay_random = 0;
3010       ei->change->delay_frames = 1;
3011     }
3012
3013     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3014     {
3015       ei->has_change_event[j] = FALSE;
3016
3017       ei->event_page_nr[j] = 0;
3018       ei->event_page[j] = &ei->change_page[0];
3019     }
3020   }
3021
3022   /* add changing elements from pre-defined list */
3023   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3024   {
3025     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3026     struct ElementInfo *ei = &element_info[ch_delay->element];
3027
3028     ei->change->target_element       = ch_delay->target_element;
3029     ei->change->delay_fixed          = ch_delay->change_delay;
3030
3031     ei->change->pre_change_function  = ch_delay->pre_change_function;
3032     ei->change->change_function      = ch_delay->change_function;
3033     ei->change->post_change_function = ch_delay->post_change_function;
3034
3035     ei->change->can_change = TRUE;
3036     ei->change->can_change_or_has_action = TRUE;
3037
3038     ei->has_change_event[CE_DELAY] = TRUE;
3039
3040     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3041     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3042   }
3043
3044   /* ---------- initialize internal run-time variables ------------- */
3045
3046   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3047   {
3048     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3049
3050     for (j = 0; j < ei->num_change_pages; j++)
3051     {
3052       ei->change_page[j].can_change_or_has_action =
3053         (ei->change_page[j].can_change |
3054          ei->change_page[j].has_action);
3055     }
3056   }
3057
3058   /* add change events from custom element configuration */
3059   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3060   {
3061     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3062
3063     for (j = 0; j < ei->num_change_pages; j++)
3064     {
3065       if (!ei->change_page[j].can_change_or_has_action)
3066         continue;
3067
3068       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3069       {
3070         /* only add event page for the first page found with this event */
3071         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3072         {
3073           ei->has_change_event[k] = TRUE;
3074
3075           ei->event_page_nr[k] = j;
3076           ei->event_page[k] = &ei->change_page[j];
3077         }
3078       }
3079     }
3080   }
3081
3082   /* ---------- initialize run-time trigger player and element ------------- */
3083
3084   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3085   {
3086     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3087
3088     for (j = 0; j < ei->num_change_pages; j++)
3089     {
3090       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3091       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3092       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3093       ei->change_page[j].actual_trigger_ce_value = 0;
3094       ei->change_page[j].actual_trigger_ce_score = 0;
3095     }
3096   }
3097
3098   /* ---------- initialize trigger events ---------------------------------- */
3099
3100   /* initialize trigger events information */
3101   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3102     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3103       trigger_events[i][j] = FALSE;
3104
3105   /* add trigger events from element change event properties */
3106   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3107   {
3108     struct ElementInfo *ei = &element_info[i];
3109
3110     for (j = 0; j < ei->num_change_pages; j++)
3111     {
3112       if (!ei->change_page[j].can_change_or_has_action)
3113         continue;
3114
3115       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3116       {
3117         int trigger_element = ei->change_page[j].trigger_element;
3118
3119         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3120         {
3121           if (ei->change_page[j].has_event[k])
3122           {
3123             if (IS_GROUP_ELEMENT(trigger_element))
3124             {
3125               struct ElementGroupInfo *group =
3126                 element_info[trigger_element].group;
3127
3128               for (l = 0; l < group->num_elements_resolved; l++)
3129                 trigger_events[group->element_resolved[l]][k] = TRUE;
3130             }
3131             else if (trigger_element == EL_ANY_ELEMENT)
3132               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3133                 trigger_events[l][k] = TRUE;
3134             else
3135               trigger_events[trigger_element][k] = TRUE;
3136           }
3137         }
3138       }
3139     }
3140   }
3141
3142   /* ---------- initialize push delay -------------------------------------- */
3143
3144   /* initialize push delay values to default */
3145   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3146   {
3147     if (!IS_CUSTOM_ELEMENT(i))
3148     {
3149       /* set default push delay values (corrected since version 3.0.7-1) */
3150       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3151       {
3152         element_info[i].push_delay_fixed = 2;
3153         element_info[i].push_delay_random = 8;
3154       }
3155       else
3156       {
3157         element_info[i].push_delay_fixed = 8;
3158         element_info[i].push_delay_random = 8;
3159       }
3160     }
3161   }
3162
3163   /* set push delay value for certain elements from pre-defined list */
3164   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3165   {
3166     int e = push_delay_list[i].element;
3167
3168     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3169     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3170   }
3171
3172   /* set push delay value for Supaplex elements for newer engine versions */
3173   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3174   {
3175     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3176     {
3177       if (IS_SP_ELEMENT(i))
3178       {
3179         /* set SP push delay to just enough to push under a falling zonk */
3180         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3181
3182         element_info[i].push_delay_fixed  = delay;
3183         element_info[i].push_delay_random = 0;
3184       }
3185     }
3186   }
3187
3188   /* ---------- initialize move stepsize ----------------------------------- */
3189
3190   /* initialize move stepsize values to default */
3191   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3192     if (!IS_CUSTOM_ELEMENT(i))
3193       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3194
3195   /* set move stepsize value for certain elements from pre-defined list */
3196   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3197   {
3198     int e = move_stepsize_list[i].element;
3199
3200     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3201   }
3202
3203   /* ---------- initialize collect score ----------------------------------- */
3204
3205   /* initialize collect score values for custom elements from initial value */
3206   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3207     if (IS_CUSTOM_ELEMENT(i))
3208       element_info[i].collect_score = element_info[i].collect_score_initial;
3209
3210   /* ---------- initialize collect count ----------------------------------- */
3211
3212   /* initialize collect count values for non-custom elements */
3213   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3214     if (!IS_CUSTOM_ELEMENT(i))
3215       element_info[i].collect_count_initial = 0;
3216
3217   /* add collect count values for all elements from pre-defined list */
3218   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3219     element_info[collect_count_list[i].element].collect_count_initial =
3220       collect_count_list[i].count;
3221
3222   /* ---------- initialize access direction -------------------------------- */
3223
3224   /* initialize access direction values to default (access from every side) */
3225   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3226     if (!IS_CUSTOM_ELEMENT(i))
3227       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3228
3229   /* set access direction value for certain elements from pre-defined list */
3230   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3231     element_info[access_direction_list[i].element].access_direction =
3232       access_direction_list[i].direction;
3233
3234   /* ---------- initialize explosion content ------------------------------- */
3235   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3236   {
3237     if (IS_CUSTOM_ELEMENT(i))
3238       continue;
3239
3240     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3241     {
3242       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3243
3244       element_info[i].content.e[x][y] =
3245         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3246          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3247          i == EL_PLAYER_3 ? EL_EMERALD :
3248          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3249          i == EL_MOLE ? EL_EMERALD_RED :
3250          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3251          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3252          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3253          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3254          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3255          i == EL_WALL_EMERALD ? EL_EMERALD :
3256          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3257          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3258          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3259          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3260          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3261          i == EL_WALL_PEARL ? EL_PEARL :
3262          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3263          EL_EMPTY);
3264     }
3265   }
3266
3267   /* ---------- initialize recursion detection ------------------------------ */
3268   recursion_loop_depth = 0;
3269   recursion_loop_detected = FALSE;
3270   recursion_loop_element = EL_UNDEFINED;
3271
3272   /* ---------- initialize graphics engine ---------------------------------- */
3273   game.scroll_delay_value =
3274     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3275      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3276   game.scroll_delay_value =
3277     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3278 }
3279
3280 int get_num_special_action(int element, int action_first, int action_last)
3281 {
3282   int num_special_action = 0;
3283   int i, j;
3284
3285   for (i = action_first; i <= action_last; i++)
3286   {
3287     boolean found = FALSE;
3288
3289     for (j = 0; j < NUM_DIRECTIONS; j++)
3290       if (el_act_dir2img(element, i, j) !=
3291           el_act_dir2img(element, ACTION_DEFAULT, j))
3292         found = TRUE;
3293
3294     if (found)
3295       num_special_action++;
3296     else
3297       break;
3298   }
3299
3300   return num_special_action;
3301 }
3302
3303
3304 /*
3305   =============================================================================
3306   InitGame()
3307   -----------------------------------------------------------------------------
3308   initialize and start new game
3309   =============================================================================
3310 */
3311
3312 void InitGame()
3313 {
3314   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3315   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3316   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3317 #if 0
3318   boolean do_fading = (game_status == GAME_MODE_MAIN);
3319 #endif
3320   int i, j, x, y;
3321
3322   game_status = GAME_MODE_PLAYING;
3323
3324   InitGameEngine();
3325   InitGameControlValues();
3326
3327   /* don't play tapes over network */
3328   network_playing = (options.network && !tape.playing);
3329
3330   for (i = 0; i < MAX_PLAYERS; i++)
3331   {
3332     struct PlayerInfo *player = &stored_player[i];
3333
3334     player->index_nr = i;
3335     player->index_bit = (1 << i);
3336     player->element_nr = EL_PLAYER_1 + i;
3337
3338     player->present = FALSE;
3339     player->active = FALSE;
3340     player->killed = FALSE;
3341
3342     player->action = 0;
3343     player->effective_action = 0;
3344     player->programmed_action = 0;
3345
3346     player->score = 0;
3347     player->score_final = 0;
3348
3349     player->gems_still_needed = level.gems_needed;
3350     player->sokobanfields_still_needed = 0;
3351     player->lights_still_needed = 0;
3352     player->friends_still_needed = 0;
3353
3354     for (j = 0; j < MAX_NUM_KEYS; j++)
3355       player->key[j] = FALSE;
3356
3357     player->num_white_keys = 0;
3358
3359     player->dynabomb_count = 0;
3360     player->dynabomb_size = 1;
3361     player->dynabombs_left = 0;
3362     player->dynabomb_xl = FALSE;
3363
3364     player->MovDir = MV_NONE;
3365     player->MovPos = 0;
3366     player->GfxPos = 0;
3367     player->GfxDir = MV_NONE;
3368     player->GfxAction = ACTION_DEFAULT;
3369     player->Frame = 0;
3370     player->StepFrame = 0;
3371
3372     player->use_murphy = FALSE;
3373     player->artwork_element =
3374       (level.use_artwork_element[i] ? level.artwork_element[i] :
3375        player->element_nr);
3376
3377     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3378     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3379
3380     player->gravity = level.initial_player_gravity[i];
3381
3382     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3383
3384     player->actual_frame_counter = 0;
3385
3386     player->step_counter = 0;
3387
3388     player->last_move_dir = MV_NONE;
3389
3390     player->is_active = FALSE;
3391
3392     player->is_waiting = FALSE;
3393     player->is_moving = FALSE;
3394     player->is_auto_moving = FALSE;
3395     player->is_digging = FALSE;
3396     player->is_snapping = FALSE;
3397     player->is_collecting = FALSE;
3398     player->is_pushing = FALSE;
3399     player->is_switching = FALSE;
3400     player->is_dropping = FALSE;
3401     player->is_dropping_pressed = FALSE;
3402
3403     player->is_bored = FALSE;
3404     player->is_sleeping = FALSE;
3405
3406     player->frame_counter_bored = -1;
3407     player->frame_counter_sleeping = -1;
3408
3409     player->anim_delay_counter = 0;
3410     player->post_delay_counter = 0;
3411
3412     player->dir_waiting = MV_NONE;
3413     player->action_waiting = ACTION_DEFAULT;
3414     player->last_action_waiting = ACTION_DEFAULT;
3415     player->special_action_bored = ACTION_DEFAULT;
3416     player->special_action_sleeping = ACTION_DEFAULT;
3417
3418     player->switch_x = -1;
3419     player->switch_y = -1;
3420
3421     player->drop_x = -1;
3422     player->drop_y = -1;
3423
3424     player->show_envelope = 0;
3425
3426     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3427
3428     player->push_delay       = -1;      /* initialized when pushing starts */
3429     player->push_delay_value = game.initial_push_delay_value;
3430
3431     player->drop_delay = 0;
3432     player->drop_pressed_delay = 0;
3433
3434     player->last_jx = -1;
3435     player->last_jy = -1;
3436     player->jx = -1;
3437     player->jy = -1;
3438
3439     player->shield_normal_time_left = 0;
3440     player->shield_deadly_time_left = 0;
3441
3442     player->inventory_infinite_element = EL_UNDEFINED;
3443     player->inventory_size = 0;
3444
3445     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3446     SnapField(player, 0, 0);
3447
3448     player->LevelSolved = FALSE;
3449     player->GameOver = FALSE;
3450
3451     player->LevelSolved_GameWon = FALSE;
3452     player->LevelSolved_GameEnd = FALSE;
3453     player->LevelSolved_PanelOff = FALSE;
3454     player->LevelSolved_SaveTape = FALSE;
3455     player->LevelSolved_SaveScore = FALSE;
3456   }
3457
3458   network_player_action_received = FALSE;
3459
3460 #if defined(NETWORK_AVALIABLE)
3461   /* initial null action */
3462   if (network_playing)
3463     SendToServer_MovePlayer(MV_NONE);
3464 #endif
3465
3466   ZX = ZY = -1;
3467   ExitX = ExitY = -1;
3468
3469   FrameCounter = 0;
3470   TimeFrames = 0;
3471   TimePlayed = 0;
3472   TimeLeft = level.time;
3473   TapeTime = 0;
3474
3475   ScreenMovDir = MV_NONE;
3476   ScreenMovPos = 0;
3477   ScreenGfxPos = 0;
3478
3479   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3480
3481   AllPlayersGone = FALSE;
3482
3483   game.yamyam_content_nr = 0;
3484   game.robot_wheel_active = FALSE;
3485   game.magic_wall_active = FALSE;
3486   game.magic_wall_time_left = 0;
3487   game.light_time_left = 0;
3488   game.timegate_time_left = 0;
3489   game.switchgate_pos = 0;
3490   game.wind_direction = level.wind_direction_initial;
3491
3492 #if !USE_PLAYER_GRAVITY
3493   game.gravity = FALSE;
3494   game.explosions_delayed = TRUE;
3495 #endif
3496
3497   game.lenses_time_left = 0;
3498   game.magnify_time_left = 0;
3499
3500   game.ball_state = level.ball_state_initial;
3501   game.ball_content_nr = 0;
3502
3503   game.envelope_active = FALSE;
3504
3505   /* set focus to local player for network games, else to all players */
3506   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3507   game.centered_player_nr_next = game.centered_player_nr;
3508   game.set_centered_player = FALSE;
3509
3510   if (network_playing && tape.recording)
3511   {
3512     /* store client dependent player focus when recording network games */
3513     tape.centered_player_nr_next = game.centered_player_nr_next;
3514     tape.set_centered_player = TRUE;
3515   }
3516
3517   for (i = 0; i < NUM_BELTS; i++)
3518   {
3519     game.belt_dir[i] = MV_NONE;
3520     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3521   }
3522
3523   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3524     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3525
3526   SCAN_PLAYFIELD(x, y)
3527   {
3528     Feld[x][y] = level.field[x][y];
3529     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3530     ChangeDelay[x][y] = 0;
3531     ChangePage[x][y] = -1;
3532 #if USE_NEW_CUSTOM_VALUE
3533     CustomValue[x][y] = 0;              /* initialized in InitField() */
3534 #endif
3535     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3536     AmoebaNr[x][y] = 0;
3537     WasJustMoving[x][y] = 0;
3538     WasJustFalling[x][y] = 0;
3539     CheckCollision[x][y] = 0;
3540     CheckImpact[x][y] = 0;
3541     Stop[x][y] = FALSE;
3542     Pushed[x][y] = FALSE;
3543
3544     ChangeCount[x][y] = 0;
3545     ChangeEvent[x][y] = -1;
3546
3547     ExplodePhase[x][y] = 0;
3548     ExplodeDelay[x][y] = 0;
3549     ExplodeField[x][y] = EX_TYPE_NONE;
3550
3551     RunnerVisit[x][y] = 0;
3552     PlayerVisit[x][y] = 0;
3553
3554     GfxFrame[x][y] = 0;
3555     GfxRandom[x][y] = INIT_GFX_RANDOM();
3556     GfxElement[x][y] = EL_UNDEFINED;
3557     GfxAction[x][y] = ACTION_DEFAULT;
3558     GfxDir[x][y] = MV_NONE;
3559   }
3560
3561   SCAN_PLAYFIELD(x, y)
3562   {
3563     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3564       emulate_bd = FALSE;
3565     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3566       emulate_sb = FALSE;
3567     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3568       emulate_sp = FALSE;
3569
3570     InitField(x, y, TRUE);
3571   }
3572
3573   InitBeltMovement();
3574
3575   for (i = 0; i < MAX_PLAYERS; i++)
3576   {
3577     struct PlayerInfo *player = &stored_player[i];
3578
3579     /* set number of special actions for bored and sleeping animation */
3580     player->num_special_action_bored =
3581       get_num_special_action(player->artwork_element,
3582                              ACTION_BORING_1, ACTION_BORING_LAST);
3583     player->num_special_action_sleeping =
3584       get_num_special_action(player->artwork_element,
3585                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3586   }
3587
3588   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3589                     emulate_sb ? EMU_SOKOBAN :
3590                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3591
3592 #if USE_NEW_ALL_SLIPPERY
3593   /* initialize type of slippery elements */
3594   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3595   {
3596     if (!IS_CUSTOM_ELEMENT(i))
3597     {
3598       /* default: elements slip down either to the left or right randomly */
3599       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3600
3601       /* SP style elements prefer to slip down on the left side */
3602       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3603         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3604
3605       /* BD style elements prefer to slip down on the left side */
3606       if (game.emulation == EMU_BOULDERDASH)
3607         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3608     }
3609   }
3610 #endif
3611
3612   /* initialize explosion and ignition delay */
3613   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3614   {
3615     if (!IS_CUSTOM_ELEMENT(i))
3616     {
3617       int num_phase = 8;
3618       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3619                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3620                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3621       int last_phase = (num_phase + 1) * delay;
3622       int half_phase = (num_phase / 2) * delay;
3623
3624       element_info[i].explosion_delay = last_phase - 1;
3625       element_info[i].ignition_delay = half_phase;
3626
3627       if (i == EL_BLACK_ORB)
3628         element_info[i].ignition_delay = 1;
3629     }
3630
3631 #if 0
3632     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3633       element_info[i].explosion_delay = 1;
3634
3635     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3636       element_info[i].ignition_delay = 1;
3637 #endif
3638   }
3639
3640   /* correct non-moving belts to start moving left */
3641   for (i = 0; i < NUM_BELTS; i++)
3642     if (game.belt_dir[i] == MV_NONE)
3643       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3644
3645   /* check if any connected player was not found in playfield */
3646   for (i = 0; i < MAX_PLAYERS; i++)
3647   {
3648     struct PlayerInfo *player = &stored_player[i];
3649
3650     if (player->connected && !player->present)
3651     {
3652       for (j = 0; j < MAX_PLAYERS; j++)
3653       {
3654         struct PlayerInfo *some_player = &stored_player[j];
3655         int jx = some_player->jx, jy = some_player->jy;
3656
3657         /* assign first free player found that is present in the playfield */
3658         if (some_player->present && !some_player->connected)
3659         {
3660           player->present = TRUE;
3661           player->active = TRUE;
3662
3663           some_player->present = FALSE;
3664           some_player->active = FALSE;
3665
3666           player->artwork_element = some_player->artwork_element;
3667
3668           player->block_last_field       = some_player->block_last_field;
3669           player->block_delay_adjustment = some_player->block_delay_adjustment;
3670
3671           StorePlayer[jx][jy] = player->element_nr;
3672           player->jx = player->last_jx = jx;
3673           player->jy = player->last_jy = jy;
3674
3675           break;
3676         }
3677       }
3678     }
3679   }
3680
3681   if (tape.playing)
3682   {
3683     /* when playing a tape, eliminate all players who do not participate */
3684
3685     for (i = 0; i < MAX_PLAYERS; i++)
3686     {
3687       if (stored_player[i].active && !tape.player_participates[i])
3688       {
3689         struct PlayerInfo *player = &stored_player[i];
3690         int jx = player->jx, jy = player->jy;
3691
3692         player->active = FALSE;
3693         StorePlayer[jx][jy] = 0;
3694         Feld[jx][jy] = EL_EMPTY;
3695       }
3696     }
3697   }
3698   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3699   {
3700     /* when in single player mode, eliminate all but the first active player */
3701
3702     for (i = 0; i < MAX_PLAYERS; i++)
3703     {
3704       if (stored_player[i].active)
3705       {
3706         for (j = i + 1; j < MAX_PLAYERS; j++)
3707         {
3708           if (stored_player[j].active)
3709           {
3710             struct PlayerInfo *player = &stored_player[j];
3711             int jx = player->jx, jy = player->jy;
3712
3713             player->active = FALSE;
3714             player->present = FALSE;
3715
3716             StorePlayer[jx][jy] = 0;
3717             Feld[jx][jy] = EL_EMPTY;
3718           }
3719         }
3720       }
3721     }
3722   }
3723
3724   /* when recording the game, store which players take part in the game */
3725   if (tape.recording)
3726   {
3727     for (i = 0; i < MAX_PLAYERS; i++)
3728       if (stored_player[i].active)
3729         tape.player_participates[i] = TRUE;
3730   }
3731
3732   if (options.debug)
3733   {
3734     for (i = 0; i < MAX_PLAYERS; i++)
3735     {
3736       struct PlayerInfo *player = &stored_player[i];
3737
3738       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3739              i+1,
3740              player->present,
3741              player->connected,
3742              player->active);
3743       if (local_player == player)
3744         printf("Player  %d is local player.\n", i+1);
3745     }
3746   }
3747
3748   if (BorderElement == EL_EMPTY)
3749   {
3750     SBX_Left = 0;
3751     SBX_Right = lev_fieldx - SCR_FIELDX;
3752     SBY_Upper = 0;
3753     SBY_Lower = lev_fieldy - SCR_FIELDY;
3754   }
3755   else
3756   {
3757     SBX_Left = -1;
3758     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3759     SBY_Upper = -1;
3760     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3761   }
3762
3763   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3764     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3765
3766   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3767     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3768
3769   /* if local player not found, look for custom element that might create
3770      the player (make some assumptions about the right custom element) */
3771   if (!local_player->present)
3772   {
3773     int start_x = 0, start_y = 0;
3774     int found_rating = 0;
3775     int found_element = EL_UNDEFINED;
3776     int player_nr = local_player->index_nr;
3777
3778     SCAN_PLAYFIELD(x, y)
3779     {
3780       int element = Feld[x][y];
3781       int content;
3782       int xx, yy;
3783       boolean is_player;
3784
3785       if (level.use_start_element[player_nr] &&
3786           level.start_element[player_nr] == element &&
3787           found_rating < 4)
3788       {
3789         start_x = x;
3790         start_y = y;
3791
3792         found_rating = 4;
3793         found_element = element;
3794       }
3795
3796       if (!IS_CUSTOM_ELEMENT(element))
3797         continue;
3798
3799       if (CAN_CHANGE(element))
3800       {
3801         for (i = 0; i < element_info[element].num_change_pages; i++)
3802         {
3803           /* check for player created from custom element as single target */
3804           content = element_info[element].change_page[i].target_element;
3805           is_player = ELEM_IS_PLAYER(content);
3806
3807           if (is_player && (found_rating < 3 ||
3808                             (found_rating == 3 && element < found_element)))
3809           {
3810             start_x = x;
3811             start_y = y;
3812
3813             found_rating = 3;
3814             found_element = element;
3815           }
3816         }
3817       }
3818
3819       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3820       {
3821         /* check for player created from custom element as explosion content */
3822         content = element_info[element].content.e[xx][yy];
3823         is_player = ELEM_IS_PLAYER(content);
3824
3825         if (is_player && (found_rating < 2 ||
3826                           (found_rating == 2 && element < found_element)))
3827         {
3828           start_x = x + xx - 1;
3829           start_y = y + yy - 1;
3830
3831           found_rating = 2;
3832           found_element = element;
3833         }
3834
3835         if (!CAN_CHANGE(element))
3836           continue;
3837
3838         for (i = 0; i < element_info[element].num_change_pages; i++)
3839         {
3840           /* check for player created from custom element as extended target */
3841           content =
3842             element_info[element].change_page[i].target_content.e[xx][yy];
3843
3844           is_player = ELEM_IS_PLAYER(content);
3845
3846           if (is_player && (found_rating < 1 ||
3847                             (found_rating == 1 && element < found_element)))
3848           {
3849             start_x = x + xx - 1;
3850             start_y = y + yy - 1;
3851
3852             found_rating = 1;
3853             found_element = element;
3854           }
3855         }
3856       }
3857     }
3858
3859     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3860                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3861                 start_x - MIDPOSX);
3862
3863     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3864                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3865                 start_y - MIDPOSY);
3866   }
3867   else
3868   {
3869     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3870                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3871                 local_player->jx - MIDPOSX);
3872
3873     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3874                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3875                 local_player->jy - MIDPOSY);
3876   }
3877
3878   /* do not use PLAYING mask for fading out from main screen */
3879   game_status = GAME_MODE_MAIN;
3880
3881   StopAnimation();
3882
3883   if (!game.restart_level)
3884     CloseDoor(DOOR_CLOSE_1);
3885
3886 #if 1
3887   if (level_editor_test_game)
3888     FadeSkipNextFadeIn();
3889   else
3890     FadeSetEnterScreen();
3891 #else
3892   if (level_editor_test_game)
3893     fading = fading_none;
3894   else
3895     fading = menu.destination;
3896 #endif
3897
3898 #if 1
3899   FadeOut(REDRAW_FIELD);
3900 #else
3901   if (do_fading)
3902     FadeOut(REDRAW_FIELD);
3903 #endif
3904
3905   game_status = GAME_MODE_PLAYING;
3906
3907   /* !!! FIX THIS (START) !!! */
3908   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3909   {
3910     InitGameEngine_EM();
3911
3912     /* blit playfield from scroll buffer to normal back buffer for fading in */
3913     BlitScreenToBitmap_EM(backbuffer);
3914   }
3915   else
3916   {
3917     DrawLevel();
3918     DrawAllPlayers();
3919
3920     /* after drawing the level, correct some elements */
3921     if (game.timegate_time_left == 0)
3922       CloseAllOpenTimegates();
3923
3924     /* blit playfield from scroll buffer to normal back buffer for fading in */
3925     if (setup.soft_scrolling)
3926       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3927
3928     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3929   }
3930   /* !!! FIX THIS (END) !!! */
3931
3932 #if 1
3933   FadeIn(REDRAW_FIELD);
3934 #else
3935   if (do_fading)
3936     FadeIn(REDRAW_FIELD);
3937
3938   BackToFront();
3939 #endif
3940
3941   if (!game.restart_level)
3942   {
3943     /* copy default game door content to main double buffer */
3944     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3945                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3946   }
3947
3948   SetPanelBackground();
3949   SetDrawBackgroundMask(REDRAW_DOOR_1);
3950
3951   UpdateGameDoorValues();
3952   DrawGameDoorValues();
3953
3954   if (!game.restart_level)
3955   {
3956     UnmapGameButtons();
3957     UnmapTapeButtons();
3958     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3959     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3960     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3961     MapGameButtons();
3962     MapTapeButtons();
3963
3964     /* copy actual game door content to door double buffer for OpenDoor() */
3965     BlitBitmap(drawto, bitmap_db_door,
3966                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3967
3968     OpenDoor(DOOR_OPEN_ALL);
3969
3970     PlaySound(SND_GAME_STARTING);
3971
3972     if (setup.sound_music)
3973       PlayLevelMusic();
3974
3975     KeyboardAutoRepeatOffUnlessAutoplay();
3976
3977     if (options.debug)
3978     {
3979       for (i = 0; i < MAX_PLAYERS; i++)
3980         printf("Player %d %sactive.\n",
3981                i + 1, (stored_player[i].active ? "" : "not "));
3982     }
3983   }
3984
3985 #if 1
3986   UnmapAllGadgets();
3987
3988   MapGameButtons();
3989   MapTapeButtons();
3990 #endif
3991
3992   game.restart_level = FALSE;
3993 }
3994
3995 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3996 {
3997   /* this is used for non-R'n'D game engines to update certain engine values */
3998
3999   /* needed to determine if sounds are played within the visible screen area */
4000   scroll_x = actual_scroll_x;
4001   scroll_y = actual_scroll_y;
4002 }
4003
4004 void InitMovDir(int x, int y)
4005 {
4006   int i, element = Feld[x][y];
4007   static int xy[4][2] =
4008   {
4009     {  0, +1 },
4010     { +1,  0 },
4011     {  0, -1 },
4012     { -1,  0 }
4013   };
4014   static int direction[3][4] =
4015   {
4016     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4017     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4018     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4019   };
4020
4021   switch (element)
4022   {
4023     case EL_BUG_RIGHT:
4024     case EL_BUG_UP:
4025     case EL_BUG_LEFT:
4026     case EL_BUG_DOWN:
4027       Feld[x][y] = EL_BUG;
4028       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4029       break;
4030
4031     case EL_SPACESHIP_RIGHT:
4032     case EL_SPACESHIP_UP:
4033     case EL_SPACESHIP_LEFT:
4034     case EL_SPACESHIP_DOWN:
4035       Feld[x][y] = EL_SPACESHIP;
4036       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4037       break;
4038
4039     case EL_BD_BUTTERFLY_RIGHT:
4040     case EL_BD_BUTTERFLY_UP:
4041     case EL_BD_BUTTERFLY_LEFT:
4042     case EL_BD_BUTTERFLY_DOWN:
4043       Feld[x][y] = EL_BD_BUTTERFLY;
4044       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4045       break;
4046
4047     case EL_BD_FIREFLY_RIGHT:
4048     case EL_BD_FIREFLY_UP:
4049     case EL_BD_FIREFLY_LEFT:
4050     case EL_BD_FIREFLY_DOWN:
4051       Feld[x][y] = EL_BD_FIREFLY;
4052       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4053       break;
4054
4055     case EL_PACMAN_RIGHT:
4056     case EL_PACMAN_UP:
4057     case EL_PACMAN_LEFT:
4058     case EL_PACMAN_DOWN:
4059       Feld[x][y] = EL_PACMAN;
4060       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4061       break;
4062
4063     case EL_YAMYAM_LEFT:
4064     case EL_YAMYAM_RIGHT:
4065     case EL_YAMYAM_UP:
4066     case EL_YAMYAM_DOWN:
4067       Feld[x][y] = EL_YAMYAM;
4068       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4069       break;
4070
4071     case EL_SP_SNIKSNAK:
4072       MovDir[x][y] = MV_UP;
4073       break;
4074
4075     case EL_SP_ELECTRON:
4076       MovDir[x][y] = MV_LEFT;
4077       break;
4078
4079     case EL_MOLE_LEFT:
4080     case EL_MOLE_RIGHT:
4081     case EL_MOLE_UP:
4082     case EL_MOLE_DOWN:
4083       Feld[x][y] = EL_MOLE;
4084       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4085       break;
4086
4087     default:
4088       if (IS_CUSTOM_ELEMENT(element))
4089       {
4090         struct ElementInfo *ei = &element_info[element];
4091         int move_direction_initial = ei->move_direction_initial;
4092         int move_pattern = ei->move_pattern;
4093
4094         if (move_direction_initial == MV_START_PREVIOUS)
4095         {
4096           if (MovDir[x][y] != MV_NONE)
4097             return;
4098
4099           move_direction_initial = MV_START_AUTOMATIC;
4100         }
4101
4102         if (move_direction_initial == MV_START_RANDOM)
4103           MovDir[x][y] = 1 << RND(4);
4104         else if (move_direction_initial & MV_ANY_DIRECTION)
4105           MovDir[x][y] = move_direction_initial;
4106         else if (move_pattern == MV_ALL_DIRECTIONS ||
4107                  move_pattern == MV_TURNING_LEFT ||
4108                  move_pattern == MV_TURNING_RIGHT ||
4109                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4110                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4111                  move_pattern == MV_TURNING_RANDOM)
4112           MovDir[x][y] = 1 << RND(4);
4113         else if (move_pattern == MV_HORIZONTAL)
4114           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4115         else if (move_pattern == MV_VERTICAL)
4116           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4117         else if (move_pattern & MV_ANY_DIRECTION)
4118           MovDir[x][y] = element_info[element].move_pattern;
4119         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4120                  move_pattern == MV_ALONG_RIGHT_SIDE)
4121         {
4122           /* use random direction as default start direction */
4123           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4124             MovDir[x][y] = 1 << RND(4);
4125
4126           for (i = 0; i < NUM_DIRECTIONS; i++)
4127           {
4128             int x1 = x + xy[i][0];
4129             int y1 = y + xy[i][1];
4130
4131             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4132             {
4133               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4134                 MovDir[x][y] = direction[0][i];
4135               else
4136                 MovDir[x][y] = direction[1][i];
4137
4138               break;
4139             }
4140           }
4141         }                
4142       }
4143       else
4144       {
4145         MovDir[x][y] = 1 << RND(4);
4146
4147         if (element != EL_BUG &&
4148             element != EL_SPACESHIP &&
4149             element != EL_BD_BUTTERFLY &&
4150             element != EL_BD_FIREFLY)
4151           break;
4152
4153         for (i = 0; i < NUM_DIRECTIONS; i++)
4154         {
4155           int x1 = x + xy[i][0];
4156           int y1 = y + xy[i][1];
4157
4158           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4159           {
4160             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4161             {
4162               MovDir[x][y] = direction[0][i];
4163               break;
4164             }
4165             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4166                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4167             {
4168               MovDir[x][y] = direction[1][i];
4169               break;
4170             }
4171           }
4172         }
4173       }
4174       break;
4175   }
4176
4177   GfxDir[x][y] = MovDir[x][y];
4178 }
4179
4180 void InitAmoebaNr(int x, int y)
4181 {
4182   int i;
4183   int group_nr = AmoebeNachbarNr(x, y);
4184
4185   if (group_nr == 0)
4186   {
4187     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4188     {
4189       if (AmoebaCnt[i] == 0)
4190       {
4191         group_nr = i;
4192         break;
4193       }
4194     }
4195   }
4196
4197   AmoebaNr[x][y] = group_nr;
4198   AmoebaCnt[group_nr]++;
4199   AmoebaCnt2[group_nr]++;
4200 }
4201
4202 static void PlayerWins(struct PlayerInfo *player)
4203 {
4204   player->LevelSolved = TRUE;
4205   player->GameOver = TRUE;
4206
4207   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4208                          level.native_em_level->lev->score : player->score);
4209 }
4210
4211 void GameWon()
4212 {
4213   static int time, time_final;
4214   static int score, score_final;
4215   static int game_over_delay_1 = 0;
4216   static int game_over_delay_2 = 0;
4217   int game_over_delay_value_1 = 50;
4218   int game_over_delay_value_2 = 50;
4219
4220   if (!local_player->LevelSolved_GameWon)
4221   {
4222     int i;
4223
4224     /* do not start end game actions before the player stops moving (to exit) */
4225     if (local_player->MovPos)
4226       return;
4227
4228     local_player->LevelSolved_GameWon = TRUE;
4229     local_player->LevelSolved_SaveTape = tape.recording;
4230     local_player->LevelSolved_SaveScore = !tape.playing;
4231
4232     if (tape.auto_play)         /* tape might already be stopped here */
4233       tape.auto_play_level_solved = TRUE;
4234
4235 #if 1
4236     TapeStop();
4237 #endif
4238
4239     game_over_delay_1 = game_over_delay_value_1;
4240     game_over_delay_2 = game_over_delay_value_2;
4241
4242     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4243     score = score_final = local_player->score_final;
4244
4245     if (TimeLeft > 0)
4246     {
4247       time_final = 0;
4248       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4249     }
4250     else if (level.time == 0 && TimePlayed < 999)
4251     {
4252       time_final = 999;
4253       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4254     }
4255
4256     local_player->score_final = score_final;
4257
4258     if (level_editor_test_game)
4259     {
4260       time = time_final;
4261       score = score_final;
4262
4263 #if 1
4264       game_panel_controls[GAME_PANEL_TIME].value = time;
4265       game_panel_controls[GAME_PANEL_SCORE].value = score;
4266
4267       DisplayGameControlValues();
4268 #else
4269       DrawGameValue_Time(time);
4270       DrawGameValue_Score(score);
4271 #endif
4272     }
4273
4274     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4275     {
4276       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4277       {
4278         /* close exit door after last player */
4279         if ((AllPlayersGone &&
4280              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4281               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4282               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4283             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4284             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4285         {
4286           int element = Feld[ExitX][ExitY];
4287
4288 #if 0
4289           if (element == EL_EM_EXIT_OPEN ||
4290               element == EL_EM_STEEL_EXIT_OPEN)
4291           {
4292             Bang(ExitX, ExitY);
4293           }
4294           else
4295 #endif
4296           {
4297             Feld[ExitX][ExitY] =
4298               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4299                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4300                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4301                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4302                EL_EM_STEEL_EXIT_CLOSING);
4303
4304             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4305           }
4306         }
4307
4308         /* player disappears */
4309         DrawLevelField(ExitX, ExitY);
4310       }
4311
4312       for (i = 0; i < MAX_PLAYERS; i++)
4313       {
4314         struct PlayerInfo *player = &stored_player[i];
4315
4316         if (player->present)
4317         {
4318           RemovePlayer(player);
4319
4320           /* player disappears */
4321           DrawLevelField(player->jx, player->jy);
4322         }
4323       }
4324     }
4325
4326     PlaySound(SND_GAME_WINNING);
4327   }
4328
4329   if (game_over_delay_1 > 0)
4330   {
4331     game_over_delay_1--;
4332
4333     return;
4334   }
4335
4336   if (time != time_final)
4337   {
4338     int time_to_go = ABS(time_final - time);
4339     int time_count_dir = (time < time_final ? +1 : -1);
4340     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4341
4342     time  += time_count_steps * time_count_dir;
4343     score += time_count_steps * level.score[SC_TIME_BONUS];
4344
4345 #if 1
4346     game_panel_controls[GAME_PANEL_TIME].value = time;
4347     game_panel_controls[GAME_PANEL_SCORE].value = score;
4348
4349     DisplayGameControlValues();
4350 #else
4351     DrawGameValue_Time(time);
4352     DrawGameValue_Score(score);
4353 #endif
4354
4355     if (time == time_final)
4356       StopSound(SND_GAME_LEVELTIME_BONUS);
4357     else if (setup.sound_loops)
4358       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4359     else
4360       PlaySound(SND_GAME_LEVELTIME_BONUS);
4361
4362     return;
4363   }
4364
4365   local_player->LevelSolved_PanelOff = TRUE;
4366
4367   if (game_over_delay_2 > 0)
4368   {
4369     game_over_delay_2--;
4370
4371     return;
4372   }
4373
4374 #if 1
4375   GameEnd();
4376 #endif
4377 }
4378
4379 void GameEnd()
4380 {
4381   int hi_pos;
4382   boolean raise_level = FALSE;
4383
4384   local_player->LevelSolved_GameEnd = TRUE;
4385
4386   CloseDoor(DOOR_CLOSE_1);
4387
4388   if (local_player->LevelSolved_SaveTape)
4389   {
4390 #if 0
4391     TapeStop();
4392 #endif
4393
4394 #if 1
4395     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4396 #else
4397     SaveTape(tape.level_nr);            /* ask to save tape */
4398 #endif
4399   }
4400
4401   if (level_editor_test_game)
4402   {
4403     game_status = GAME_MODE_MAIN;
4404
4405 #if 1
4406     DrawAndFadeInMainMenu(REDRAW_FIELD);
4407 #else
4408     DrawMainMenu();
4409 #endif
4410
4411     return;
4412   }
4413
4414   if (!local_player->LevelSolved_SaveScore)
4415   {
4416 #if 1
4417     FadeOut(REDRAW_FIELD);
4418 #endif
4419
4420     game_status = GAME_MODE_MAIN;
4421
4422     DrawAndFadeInMainMenu(REDRAW_FIELD);
4423
4424     return;
4425   }
4426
4427   if (level_nr == leveldir_current->handicap_level)
4428   {
4429     leveldir_current->handicap_level++;
4430     SaveLevelSetup_SeriesInfo();
4431   }
4432
4433   if (level_nr < leveldir_current->last_level)
4434     raise_level = TRUE;                 /* advance to next level */
4435
4436   if ((hi_pos = NewHiScore()) >= 0) 
4437   {
4438     game_status = GAME_MODE_SCORES;
4439
4440     DrawHallOfFame(hi_pos);
4441
4442     if (raise_level)
4443     {
4444       level_nr++;
4445       TapeErase();
4446     }
4447   }
4448   else
4449   {
4450 #if 1
4451     FadeOut(REDRAW_FIELD);
4452 #endif
4453
4454     game_status = GAME_MODE_MAIN;
4455
4456     if (raise_level)
4457     {
4458       level_nr++;
4459       TapeErase();
4460     }
4461
4462     DrawAndFadeInMainMenu(REDRAW_FIELD);
4463   }
4464 }
4465
4466 int NewHiScore()
4467 {
4468   int k, l;
4469   int position = -1;
4470
4471   LoadScore(level_nr);
4472
4473   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4474       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4475     return -1;
4476
4477   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4478   {
4479     if (local_player->score_final > highscore[k].Score)
4480     {
4481       /* player has made it to the hall of fame */
4482
4483       if (k < MAX_SCORE_ENTRIES - 1)
4484       {
4485         int m = MAX_SCORE_ENTRIES - 1;
4486
4487 #ifdef ONE_PER_NAME
4488         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4489           if (strEqual(setup.player_name, highscore[l].Name))
4490             m = l;
4491         if (m == k)     /* player's new highscore overwrites his old one */
4492           goto put_into_list;
4493 #endif
4494
4495         for (l = m; l > k; l--)
4496         {
4497           strcpy(highscore[l].Name, highscore[l - 1].Name);
4498           highscore[l].Score = highscore[l - 1].Score;
4499         }
4500       }
4501
4502 #ifdef ONE_PER_NAME
4503       put_into_list:
4504 #endif
4505       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4506       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4507       highscore[k].Score = local_player->score_final; 
4508       position = k;
4509       break;
4510     }
4511
4512 #ifdef ONE_PER_NAME
4513     else if (!strncmp(setup.player_name, highscore[k].Name,
4514                       MAX_PLAYER_NAME_LEN))
4515       break;    /* player already there with a higher score */
4516 #endif
4517
4518   }
4519
4520   if (position >= 0) 
4521     SaveScore(level_nr);
4522
4523   return position;
4524 }
4525
4526 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4527 {
4528   int element = Feld[x][y];
4529   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4530   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4531   int horiz_move = (dx != 0);
4532   int sign = (horiz_move ? dx : dy);
4533   int step = sign * element_info[element].move_stepsize;
4534
4535   /* special values for move stepsize for spring and things on conveyor belt */
4536   if (horiz_move)
4537   {
4538     if (CAN_FALL(element) &&
4539         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4540       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4541     else if (element == EL_SPRING)
4542       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4543   }
4544
4545   return step;
4546 }
4547
4548 inline static int getElementMoveStepsize(int x, int y)
4549 {
4550   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4551 }
4552
4553 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4554 {
4555   if (player->GfxAction != action || player->GfxDir != dir)
4556   {
4557 #if 0
4558     printf("Player frame reset! (%d => %d, %d => %d)\n",
4559            player->GfxAction, action, player->GfxDir, dir);
4560 #endif
4561
4562     player->GfxAction = action;
4563     player->GfxDir = dir;
4564     player->Frame = 0;
4565     player->StepFrame = 0;
4566   }
4567 }
4568
4569 #if USE_GFX_RESET_GFX_ANIMATION
4570 static void ResetGfxFrame(int x, int y, boolean redraw)
4571 {
4572   int element = Feld[x][y];
4573   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4574   int last_gfx_frame = GfxFrame[x][y];
4575
4576   if (graphic_info[graphic].anim_global_sync)
4577     GfxFrame[x][y] = FrameCounter;
4578   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4579     GfxFrame[x][y] = CustomValue[x][y];
4580   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4581     GfxFrame[x][y] = element_info[element].collect_score;
4582   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4583     GfxFrame[x][y] = ChangeDelay[x][y];
4584
4585   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4586     DrawLevelGraphicAnimation(x, y, graphic);
4587 }
4588 #endif
4589
4590 static void ResetGfxAnimation(int x, int y)
4591 {
4592   GfxAction[x][y] = ACTION_DEFAULT;
4593   GfxDir[x][y] = MovDir[x][y];
4594   GfxFrame[x][y] = 0;
4595
4596 #if USE_GFX_RESET_GFX_ANIMATION
4597   ResetGfxFrame(x, y, FALSE);
4598 #endif
4599 }
4600
4601 static void ResetRandomAnimationValue(int x, int y)
4602 {
4603   GfxRandom[x][y] = INIT_GFX_RANDOM();
4604 }
4605
4606 void InitMovingField(int x, int y, int direction)
4607 {
4608   int element = Feld[x][y];
4609   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4610   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4611   int newx = x + dx;
4612   int newy = y + dy;
4613   boolean is_moving_before, is_moving_after;
4614 #if 0
4615   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4616 #endif
4617
4618   /* check if element was/is moving or being moved before/after mode change */
4619 #if 1
4620 #if 1
4621   is_moving_before = (WasJustMoving[x][y] != 0);
4622 #else
4623   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4624   is_moving_before = WasJustMoving[x][y];
4625 #endif
4626 #else
4627   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4628 #endif
4629   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4630
4631   /* reset animation only for moving elements which change direction of moving
4632      or which just started or stopped moving
4633      (else CEs with property "can move" / "not moving" are reset each frame) */
4634 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4635 #if 1
4636   if (is_moving_before != is_moving_after ||
4637       direction != MovDir[x][y])
4638     ResetGfxAnimation(x, y);
4639 #else
4640   if ((is_moving_before || is_moving_after) && !continues_moving)
4641     ResetGfxAnimation(x, y);
4642 #endif
4643 #else
4644   if (!continues_moving)
4645     ResetGfxAnimation(x, y);
4646 #endif
4647
4648   MovDir[x][y] = direction;
4649   GfxDir[x][y] = direction;
4650
4651 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4652   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4653                      direction == MV_DOWN && CAN_FALL(element) ?
4654                      ACTION_FALLING : ACTION_MOVING);
4655 #else
4656   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4657                      ACTION_FALLING : ACTION_MOVING);
4658 #endif
4659
4660   /* this is needed for CEs with property "can move" / "not moving" */
4661
4662   if (is_moving_after)
4663   {
4664     if (Feld[newx][newy] == EL_EMPTY)
4665       Feld[newx][newy] = EL_BLOCKED;
4666
4667     MovDir[newx][newy] = MovDir[x][y];
4668
4669 #if USE_NEW_CUSTOM_VALUE
4670     CustomValue[newx][newy] = CustomValue[x][y];
4671 #endif
4672
4673     GfxFrame[newx][newy] = GfxFrame[x][y];
4674     GfxRandom[newx][newy] = GfxRandom[x][y];
4675     GfxAction[newx][newy] = GfxAction[x][y];
4676     GfxDir[newx][newy] = GfxDir[x][y];
4677   }
4678 }
4679
4680 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4681 {
4682   int direction = MovDir[x][y];
4683   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4684   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4685
4686   *goes_to_x = newx;
4687   *goes_to_y = newy;
4688 }
4689
4690 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4691 {
4692   int oldx = x, oldy = y;
4693   int direction = MovDir[x][y];
4694
4695   if (direction == MV_LEFT)
4696     oldx++;
4697   else if (direction == MV_RIGHT)
4698     oldx--;
4699   else if (direction == MV_UP)
4700     oldy++;
4701   else if (direction == MV_DOWN)
4702     oldy--;
4703
4704   *comes_from_x = oldx;
4705   *comes_from_y = oldy;
4706 }
4707
4708 int MovingOrBlocked2Element(int x, int y)
4709 {
4710   int element = Feld[x][y];
4711
4712   if (element == EL_BLOCKED)
4713   {
4714     int oldx, oldy;
4715
4716     Blocked2Moving(x, y, &oldx, &oldy);
4717     return Feld[oldx][oldy];
4718   }
4719   else
4720     return element;
4721 }
4722
4723 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4724 {
4725   /* like MovingOrBlocked2Element(), but if element is moving
4726      and (x,y) is the field the moving element is just leaving,
4727      return EL_BLOCKED instead of the element value */
4728   int element = Feld[x][y];
4729
4730   if (IS_MOVING(x, y))
4731   {
4732     if (element == EL_BLOCKED)
4733     {
4734       int oldx, oldy;
4735
4736       Blocked2Moving(x, y, &oldx, &oldy);
4737       return Feld[oldx][oldy];
4738     }
4739     else
4740       return EL_BLOCKED;
4741   }
4742   else
4743     return element;
4744 }
4745
4746 static void RemoveField(int x, int y)
4747 {
4748   Feld[x][y] = EL_EMPTY;
4749
4750   MovPos[x][y] = 0;
4751   MovDir[x][y] = 0;
4752   MovDelay[x][y] = 0;
4753
4754 #if USE_NEW_CUSTOM_VALUE
4755   CustomValue[x][y] = 0;
4756 #endif
4757
4758   AmoebaNr[x][y] = 0;
4759   ChangeDelay[x][y] = 0;
4760   ChangePage[x][y] = -1;
4761   Pushed[x][y] = FALSE;
4762
4763 #if 0
4764   ExplodeField[x][y] = EX_TYPE_NONE;
4765 #endif
4766
4767   GfxElement[x][y] = EL_UNDEFINED;
4768   GfxAction[x][y] = ACTION_DEFAULT;
4769   GfxDir[x][y] = MV_NONE;
4770 }
4771
4772 void RemoveMovingField(int x, int y)
4773 {
4774   int oldx = x, oldy = y, newx = x, newy = y;
4775   int element = Feld[x][y];
4776   int next_element = EL_UNDEFINED;
4777
4778   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4779     return;
4780
4781   if (IS_MOVING(x, y))
4782   {
4783     Moving2Blocked(x, y, &newx, &newy);
4784
4785     if (Feld[newx][newy] != EL_BLOCKED)
4786     {
4787       /* element is moving, but target field is not free (blocked), but
4788          already occupied by something different (example: acid pool);
4789          in this case, only remove the moving field, but not the target */
4790
4791       RemoveField(oldx, oldy);
4792
4793       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4794
4795       DrawLevelField(oldx, oldy);
4796
4797       return;
4798     }
4799   }
4800   else if (element == EL_BLOCKED)
4801   {
4802     Blocked2Moving(x, y, &oldx, &oldy);
4803     if (!IS_MOVING(oldx, oldy))
4804       return;
4805   }
4806
4807   if (element == EL_BLOCKED &&
4808       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4809        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4810        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4811        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4812        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4813        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4814     next_element = get_next_element(Feld[oldx][oldy]);
4815
4816   RemoveField(oldx, oldy);
4817   RemoveField(newx, newy);
4818
4819   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4820
4821   if (next_element != EL_UNDEFINED)
4822     Feld[oldx][oldy] = next_element;
4823
4824   DrawLevelField(oldx, oldy);
4825   DrawLevelField(newx, newy);
4826 }
4827
4828 void DrawDynamite(int x, int y)
4829 {
4830   int sx = SCREENX(x), sy = SCREENY(y);
4831   int graphic = el2img(Feld[x][y]);
4832   int frame;
4833
4834   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4835     return;
4836
4837   if (IS_WALKABLE_INSIDE(Back[x][y]))
4838     return;
4839
4840   if (Back[x][y])
4841     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4842   else if (Store[x][y])
4843     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4844
4845   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4846
4847   if (Back[x][y] || Store[x][y])
4848     DrawGraphicThruMask(sx, sy, graphic, frame);
4849   else
4850     DrawGraphic(sx, sy, graphic, frame);
4851 }
4852
4853 void CheckDynamite(int x, int y)
4854 {
4855   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4856   {
4857     MovDelay[x][y]--;
4858
4859     if (MovDelay[x][y] != 0)
4860     {
4861       DrawDynamite(x, y);
4862       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4863
4864       return;
4865     }
4866   }
4867
4868   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4869
4870   Bang(x, y);
4871 }
4872
4873 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4874 {
4875   boolean num_checked_players = 0;
4876   int i;
4877
4878   for (i = 0; i < MAX_PLAYERS; i++)
4879   {
4880     if (stored_player[i].active)
4881     {
4882       int sx = stored_player[i].jx;
4883       int sy = stored_player[i].jy;
4884
4885       if (num_checked_players == 0)
4886       {
4887         *sx1 = *sx2 = sx;
4888         *sy1 = *sy2 = sy;
4889       }
4890       else
4891       {
4892         *sx1 = MIN(*sx1, sx);
4893         *sy1 = MIN(*sy1, sy);
4894         *sx2 = MAX(*sx2, sx);
4895         *sy2 = MAX(*sy2, sy);
4896       }
4897
4898       num_checked_players++;
4899     }
4900   }
4901 }
4902
4903 static boolean checkIfAllPlayersFitToScreen_RND()
4904 {
4905   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4906
4907   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4908
4909   return (sx2 - sx1 < SCR_FIELDX &&
4910           sy2 - sy1 < SCR_FIELDY);
4911 }
4912
4913 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4914 {
4915   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4916
4917   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4918
4919   *sx = (sx1 + sx2) / 2;
4920   *sy = (sy1 + sy2) / 2;
4921 }
4922
4923 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4924                         boolean center_screen, boolean quick_relocation)
4925 {
4926   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4927   boolean no_delay = (tape.warp_forward);
4928   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4929   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4930
4931   if (quick_relocation)
4932   {
4933     int offset = game.scroll_delay_value;
4934
4935     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4936     {
4937       if (!level.shifted_relocation || center_screen)
4938       {
4939         /* quick relocation (without scrolling), with centering of screen */
4940
4941         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4942                     x > SBX_Right + MIDPOSX ? SBX_Right :
4943                     x - MIDPOSX);
4944
4945         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4946                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4947                     y - MIDPOSY);
4948       }
4949       else
4950       {
4951         /* quick relocation (without scrolling), but do not center screen */
4952
4953         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4954                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4955                                old_x - MIDPOSX);
4956
4957         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4958                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4959                                old_y - MIDPOSY);
4960
4961         int offset_x = x + (scroll_x - center_scroll_x);
4962         int offset_y = y + (scroll_y - center_scroll_y);
4963
4964         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4965                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4966                     offset_x - MIDPOSX);
4967
4968         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4969                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4970                     offset_y - MIDPOSY);
4971       }
4972     }
4973     else
4974     {
4975       /* quick relocation (without scrolling), inside visible screen area */
4976
4977       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4978           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4979         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4980
4981       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4982           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4983         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4984
4985       /* don't scroll over playfield boundaries */
4986       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4987         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4988
4989       /* don't scroll over playfield boundaries */
4990       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4991         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4992     }
4993
4994     RedrawPlayfield(TRUE, 0,0,0,0);
4995   }
4996   else
4997   {
4998 #if 1
4999     int scroll_xx, scroll_yy;
5000
5001     if (!level.shifted_relocation || center_screen)
5002     {
5003       /* visible relocation (with scrolling), with centering of screen */
5004
5005       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5006                    x > SBX_Right + MIDPOSX ? SBX_Right :
5007                    x - MIDPOSX);
5008
5009       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5010                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5011                    y - MIDPOSY);
5012     }
5013     else
5014     {
5015       /* visible relocation (with scrolling), but do not center screen */
5016
5017       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5018                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5019                              old_x - MIDPOSX);
5020
5021       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5022                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5023                              old_y - MIDPOSY);
5024
5025       int offset_x = x + (scroll_x - center_scroll_x);
5026       int offset_y = y + (scroll_y - center_scroll_y);
5027
5028       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5029                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5030                    offset_x - MIDPOSX);
5031
5032       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5033                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5034                    offset_y - MIDPOSY);
5035     }
5036
5037 #else
5038
5039     /* visible relocation (with scrolling), with centering of screen */
5040
5041     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5042                      x > SBX_Right + MIDPOSX ? SBX_Right :
5043                      x - MIDPOSX);
5044
5045     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5046                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5047                      y - MIDPOSY);
5048 #endif
5049
5050     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5051
5052     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5053     {
5054       int dx = 0, dy = 0;
5055       int fx = FX, fy = FY;
5056
5057       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5058       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5059
5060       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5061         break;
5062
5063       scroll_x -= dx;
5064       scroll_y -= dy;
5065
5066       fx += dx * TILEX / 2;
5067       fy += dy * TILEY / 2;
5068
5069       ScrollLevel(dx, dy);
5070       DrawAllPlayers();
5071
5072       /* scroll in two steps of half tile size to make things smoother */
5073       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5074       FlushDisplay();
5075       Delay(wait_delay_value);
5076
5077       /* scroll second step to align at full tile size */
5078       BackToFront();
5079       Delay(wait_delay_value);
5080     }
5081
5082     DrawAllPlayers();
5083     BackToFront();
5084     Delay(wait_delay_value);
5085   }
5086 }
5087
5088 void RelocatePlayer(int jx, int jy, int el_player_raw)
5089 {
5090   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5091   int player_nr = GET_PLAYER_NR(el_player);
5092   struct PlayerInfo *player = &stored_player[player_nr];
5093   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5094   boolean no_delay = (tape.warp_forward);
5095   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5096   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5097   int old_jx = player->jx;
5098   int old_jy = player->jy;
5099   int old_element = Feld[old_jx][old_jy];
5100   int element = Feld[jx][jy];
5101   boolean player_relocated = (old_jx != jx || old_jy != jy);
5102
5103   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5104   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5105   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5106   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5107   int leave_side_horiz = move_dir_horiz;
5108   int leave_side_vert  = move_dir_vert;
5109   int enter_side = enter_side_horiz | enter_side_vert;
5110   int leave_side = leave_side_horiz | leave_side_vert;
5111
5112   if (player->GameOver)         /* do not reanimate dead player */
5113     return;
5114
5115   if (!player_relocated)        /* no need to relocate the player */
5116     return;
5117
5118   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5119   {
5120     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5121     DrawLevelField(jx, jy);
5122   }
5123
5124   if (player->present)
5125   {
5126     while (player->MovPos)
5127     {
5128       ScrollPlayer(player, SCROLL_GO_ON);
5129       ScrollScreen(NULL, SCROLL_GO_ON);
5130
5131       AdvanceFrameAndPlayerCounters(player->index_nr);
5132
5133       DrawPlayer(player);
5134
5135       BackToFront();
5136       Delay(wait_delay_value);
5137     }
5138
5139     DrawPlayer(player);         /* needed here only to cleanup last field */
5140     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5141
5142     player->is_moving = FALSE;
5143   }
5144
5145   if (IS_CUSTOM_ELEMENT(old_element))
5146     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5147                                CE_LEFT_BY_PLAYER,
5148                                player->index_bit, leave_side);
5149
5150   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5151                                       CE_PLAYER_LEAVES_X,
5152                                       player->index_bit, leave_side);
5153
5154   Feld[jx][jy] = el_player;
5155   InitPlayerField(jx, jy, el_player, TRUE);
5156
5157   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5158   {
5159     Feld[jx][jy] = element;
5160     InitField(jx, jy, FALSE);
5161   }
5162
5163   /* only visually relocate centered player */
5164   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5165                      FALSE, level.instant_relocation);
5166
5167   TestIfPlayerTouchesBadThing(jx, jy);
5168   TestIfPlayerTouchesCustomElement(jx, jy);
5169
5170   if (IS_CUSTOM_ELEMENT(element))
5171     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5172                                player->index_bit, enter_side);
5173
5174   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5175                                       player->index_bit, enter_side);
5176 }
5177
5178 void Explode(int ex, int ey, int phase, int mode)
5179 {
5180   int x, y;
5181   int last_phase;
5182   int border_element;
5183
5184   /* !!! eliminate this variable !!! */
5185   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5186
5187   if (game.explosions_delayed)
5188   {
5189     ExplodeField[ex][ey] = mode;
5190     return;
5191   }
5192
5193   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5194   {
5195     int center_element = Feld[ex][ey];
5196     int artwork_element, explosion_element;     /* set these values later */
5197
5198 #if 0
5199     /* --- This is only really needed (and now handled) in "Impact()". --- */
5200     /* do not explode moving elements that left the explode field in time */
5201     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5202         center_element == EL_EMPTY &&
5203         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5204       return;
5205 #endif
5206
5207 #if 0
5208     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5209     if (mode == EX_TYPE_NORMAL ||
5210         mode == EX_TYPE_CENTER ||
5211         mode == EX_TYPE_CROSS)
5212       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5213 #endif
5214
5215     /* remove things displayed in background while burning dynamite */
5216     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5217       Back[ex][ey] = 0;
5218
5219     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5220     {
5221       /* put moving element to center field (and let it explode there) */
5222       center_element = MovingOrBlocked2Element(ex, ey);
5223       RemoveMovingField(ex, ey);
5224       Feld[ex][ey] = center_element;
5225     }
5226
5227     /* now "center_element" is finally determined -- set related values now */
5228     artwork_element = center_element;           /* for custom player artwork */
5229     explosion_element = center_element;         /* for custom player artwork */
5230
5231     if (IS_PLAYER(ex, ey))
5232     {
5233       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5234
5235       artwork_element = stored_player[player_nr].artwork_element;
5236
5237       if (level.use_explosion_element[player_nr])
5238       {
5239         explosion_element = level.explosion_element[player_nr];
5240         artwork_element = explosion_element;
5241       }
5242     }
5243
5244 #if 1
5245     if (mode == EX_TYPE_NORMAL ||
5246         mode == EX_TYPE_CENTER ||
5247         mode == EX_TYPE_CROSS)
5248       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5249 #endif
5250
5251     last_phase = element_info[explosion_element].explosion_delay + 1;
5252
5253     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5254     {
5255       int xx = x - ex + 1;
5256       int yy = y - ey + 1;
5257       int element;
5258
5259       if (!IN_LEV_FIELD(x, y) ||
5260           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5261           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5262         continue;
5263
5264       element = Feld[x][y];
5265
5266       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5267       {
5268         element = MovingOrBlocked2Element(x, y);
5269
5270         if (!IS_EXPLOSION_PROOF(element))
5271           RemoveMovingField(x, y);
5272       }
5273
5274       /* indestructible elements can only explode in center (but not flames) */
5275       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5276                                            mode == EX_TYPE_BORDER)) ||
5277           element == EL_FLAMES)
5278         continue;
5279
5280       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5281          behaviour, for example when touching a yamyam that explodes to rocks
5282          with active deadly shield, a rock is created under the player !!! */
5283       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5284 #if 0
5285       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5286           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5287            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5288 #else
5289       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5290 #endif
5291       {
5292         if (IS_ACTIVE_BOMB(element))
5293         {
5294           /* re-activate things under the bomb like gate or penguin */
5295           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5296           Back[x][y] = 0;
5297         }
5298
5299         continue;
5300       }
5301
5302       /* save walkable background elements while explosion on same tile */
5303       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5304           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5305         Back[x][y] = element;
5306
5307       /* ignite explodable elements reached by other explosion */
5308       if (element == EL_EXPLOSION)
5309         element = Store2[x][y];
5310
5311       if (AmoebaNr[x][y] &&
5312           (element == EL_AMOEBA_FULL ||
5313            element == EL_BD_AMOEBA ||
5314            element == EL_AMOEBA_GROWING))
5315       {
5316         AmoebaCnt[AmoebaNr[x][y]]--;
5317         AmoebaCnt2[AmoebaNr[x][y]]--;
5318       }
5319
5320       RemoveField(x, y);
5321
5322       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5323       {
5324         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5325
5326         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5327
5328         if (PLAYERINFO(ex, ey)->use_murphy)
5329           Store[x][y] = EL_EMPTY;
5330       }
5331
5332       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5333          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5334       else if (ELEM_IS_PLAYER(center_element))
5335         Store[x][y] = EL_EMPTY;
5336       else if (center_element == EL_YAMYAM)
5337         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5338       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5339         Store[x][y] = element_info[center_element].content.e[xx][yy];
5340 #if 1
5341       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5342          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5343          otherwise) -- FIX THIS !!! */
5344       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5345         Store[x][y] = element_info[element].content.e[1][1];
5346 #else
5347       else if (!CAN_EXPLODE(element))
5348         Store[x][y] = element_info[element].content.e[1][1];
5349 #endif
5350       else
5351         Store[x][y] = EL_EMPTY;
5352
5353       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5354           center_element == EL_AMOEBA_TO_DIAMOND)
5355         Store2[x][y] = element;
5356
5357       Feld[x][y] = EL_EXPLOSION;
5358       GfxElement[x][y] = artwork_element;
5359
5360       ExplodePhase[x][y] = 1;
5361       ExplodeDelay[x][y] = last_phase;
5362
5363       Stop[x][y] = TRUE;
5364     }
5365
5366     if (center_element == EL_YAMYAM)
5367       game.yamyam_content_nr =
5368         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5369
5370     return;
5371   }
5372
5373   if (Stop[ex][ey])
5374     return;
5375
5376   x = ex;
5377   y = ey;
5378
5379   if (phase == 1)
5380     GfxFrame[x][y] = 0;         /* restart explosion animation */
5381
5382   last_phase = ExplodeDelay[x][y];
5383
5384   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5385
5386 #ifdef DEBUG
5387
5388   /* activate this even in non-DEBUG version until cause for crash in
5389      getGraphicAnimationFrame() (see below) is found and eliminated */
5390
5391 #endif
5392 #if 1
5393
5394 #if 1
5395   /* this can happen if the player leaves an explosion just in time */
5396   if (GfxElement[x][y] == EL_UNDEFINED)
5397     GfxElement[x][y] = EL_EMPTY;
5398 #else
5399   if (GfxElement[x][y] == EL_UNDEFINED)
5400   {
5401     printf("\n\n");
5402     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5403     printf("Explode(): This should never happen!\n");
5404     printf("\n\n");
5405
5406     GfxElement[x][y] = EL_EMPTY;
5407   }
5408 #endif
5409
5410 #endif
5411
5412   border_element = Store2[x][y];
5413   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5414     border_element = StorePlayer[x][y];
5415
5416   if (phase == element_info[border_element].ignition_delay ||
5417       phase == last_phase)
5418   {
5419     boolean border_explosion = FALSE;
5420
5421     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5422         !PLAYER_EXPLOSION_PROTECTED(x, y))
5423     {
5424       KillPlayerUnlessExplosionProtected(x, y);
5425       border_explosion = TRUE;
5426     }
5427     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5428     {
5429       Feld[x][y] = Store2[x][y];
5430       Store2[x][y] = 0;
5431       Bang(x, y);
5432       border_explosion = TRUE;
5433     }
5434     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5435     {
5436       AmoebeUmwandeln(x, y);
5437       Store2[x][y] = 0;
5438       border_explosion = TRUE;
5439     }
5440
5441     /* if an element just explodes due to another explosion (chain-reaction),
5442        do not immediately end the new explosion when it was the last frame of
5443        the explosion (as it would be done in the following "if"-statement!) */
5444     if (border_explosion && phase == last_phase)
5445       return;
5446   }
5447
5448   if (phase == last_phase)
5449   {
5450     int element;
5451
5452     element = Feld[x][y] = Store[x][y];
5453     Store[x][y] = Store2[x][y] = 0;
5454     GfxElement[x][y] = EL_UNDEFINED;
5455
5456     /* player can escape from explosions and might therefore be still alive */
5457     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5458         element <= EL_PLAYER_IS_EXPLODING_4)
5459     {
5460       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5461       int explosion_element = EL_PLAYER_1 + player_nr;
5462       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5463       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5464
5465       if (level.use_explosion_element[player_nr])
5466         explosion_element = level.explosion_element[player_nr];
5467
5468       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5469                     element_info[explosion_element].content.e[xx][yy]);
5470     }
5471
5472     /* restore probably existing indestructible background element */
5473     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5474       element = Feld[x][y] = Back[x][y];
5475     Back[x][y] = 0;
5476
5477     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5478     GfxDir[x][y] = MV_NONE;
5479     ChangeDelay[x][y] = 0;
5480     ChangePage[x][y] = -1;
5481
5482 #if USE_NEW_CUSTOM_VALUE
5483     CustomValue[x][y] = 0;
5484 #endif
5485
5486     InitField_WithBug2(x, y, FALSE);
5487
5488     DrawLevelField(x, y);
5489
5490     TestIfElementTouchesCustomElement(x, y);
5491
5492     if (GFX_CRUMBLED(element))
5493       DrawLevelFieldCrumbledSandNeighbours(x, y);
5494
5495     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5496       StorePlayer[x][y] = 0;
5497
5498     if (ELEM_IS_PLAYER(element))
5499       RelocatePlayer(x, y, element);
5500   }
5501   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5502   {
5503     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5504     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5505
5506     if (phase == delay)
5507       DrawLevelFieldCrumbledSand(x, y);
5508
5509     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5510     {
5511       DrawLevelElement(x, y, Back[x][y]);
5512       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5513     }
5514     else if (IS_WALKABLE_UNDER(Back[x][y]))
5515     {
5516       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5517       DrawLevelElementThruMask(x, y, Back[x][y]);
5518     }
5519     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5520       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5521   }
5522 }
5523
5524 void DynaExplode(int ex, int ey)
5525 {
5526   int i, j;
5527   int dynabomb_element = Feld[ex][ey];
5528   int dynabomb_size = 1;
5529   boolean dynabomb_xl = FALSE;
5530   struct PlayerInfo *player;
5531   static int xy[4][2] =
5532   {
5533     { 0, -1 },
5534     { -1, 0 },
5535     { +1, 0 },
5536     { 0, +1 }
5537   };
5538
5539   if (IS_ACTIVE_BOMB(dynabomb_element))
5540   {
5541     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5542     dynabomb_size = player->dynabomb_size;
5543     dynabomb_xl = player->dynabomb_xl;
5544     player->dynabombs_left++;
5545   }
5546
5547   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5548
5549   for (i = 0; i < NUM_DIRECTIONS; i++)
5550   {
5551     for (j = 1; j <= dynabomb_size; j++)
5552     {
5553       int x = ex + j * xy[i][0];
5554       int y = ey + j * xy[i][1];
5555       int element;
5556
5557       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5558         break;
5559
5560       element = Feld[x][y];
5561
5562       /* do not restart explosions of fields with active bombs */
5563       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5564         continue;
5565
5566       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5567
5568       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5569           !IS_DIGGABLE(element) && !dynabomb_xl)
5570         break;
5571     }
5572   }
5573 }
5574
5575 void Bang(int x, int y)
5576 {
5577   int element = MovingOrBlocked2Element(x, y);
5578   int explosion_type = EX_TYPE_NORMAL;
5579
5580   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5581   {
5582     struct PlayerInfo *player = PLAYERINFO(x, y);
5583
5584     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5585                             player->element_nr);
5586
5587     if (level.use_explosion_element[player->index_nr])
5588     {
5589       int explosion_element = level.explosion_element[player->index_nr];
5590
5591       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5592         explosion_type = EX_TYPE_CROSS;
5593       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5594         explosion_type = EX_TYPE_CENTER;
5595     }
5596   }
5597
5598   switch (element)
5599   {
5600     case EL_BUG:
5601     case EL_SPACESHIP:
5602     case EL_BD_BUTTERFLY:
5603     case EL_BD_FIREFLY:
5604     case EL_YAMYAM:
5605     case EL_DARK_YAMYAM:
5606     case EL_ROBOT:
5607     case EL_PACMAN:
5608     case EL_MOLE:
5609       RaiseScoreElement(element);
5610       break;
5611
5612     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5613     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5614     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5615     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5616     case EL_DYNABOMB_INCREASE_NUMBER:
5617     case EL_DYNABOMB_INCREASE_SIZE:
5618     case EL_DYNABOMB_INCREASE_POWER:
5619       explosion_type = EX_TYPE_DYNA;
5620       break;
5621
5622     case EL_DC_LANDMINE:
5623 #if 0
5624     case EL_EM_EXIT_OPEN:
5625     case EL_EM_STEEL_EXIT_OPEN:
5626 #endif
5627       explosion_type = EX_TYPE_CENTER;
5628       break;
5629
5630     case EL_PENGUIN:
5631     case EL_LAMP:
5632     case EL_LAMP_ACTIVE:
5633     case EL_AMOEBA_TO_DIAMOND:
5634       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5635         explosion_type = EX_TYPE_CENTER;
5636       break;
5637
5638     default:
5639       if (element_info[element].explosion_type == EXPLODES_CROSS)
5640         explosion_type = EX_TYPE_CROSS;
5641       else if (element_info[element].explosion_type == EXPLODES_1X1)
5642         explosion_type = EX_TYPE_CENTER;
5643       break;
5644   }
5645
5646   if (explosion_type == EX_TYPE_DYNA)
5647     DynaExplode(x, y);
5648   else
5649     Explode(x, y, EX_PHASE_START, explosion_type);
5650
5651   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5652 }
5653
5654 void SplashAcid(int x, int y)
5655 {
5656   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5657       (!IN_LEV_FIELD(x - 1, y - 2) ||
5658        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5659     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5660
5661   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5662       (!IN_LEV_FIELD(x + 1, y - 2) ||
5663        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5664     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5665
5666   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5667 }
5668
5669 static void InitBeltMovement()
5670 {
5671   static int belt_base_element[4] =
5672   {
5673     EL_CONVEYOR_BELT_1_LEFT,
5674     EL_CONVEYOR_BELT_2_LEFT,
5675     EL_CONVEYOR_BELT_3_LEFT,
5676     EL_CONVEYOR_BELT_4_LEFT
5677   };
5678   static int belt_base_active_element[4] =
5679   {
5680     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5681     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5682     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5683     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5684   };
5685
5686   int x, y, i, j;
5687
5688   /* set frame order for belt animation graphic according to belt direction */
5689   for (i = 0; i < NUM_BELTS; i++)
5690   {
5691     int belt_nr = i;
5692
5693     for (j = 0; j < NUM_BELT_PARTS; j++)
5694     {
5695       int element = belt_base_active_element[belt_nr] + j;
5696       int graphic_1 = el2img(element);
5697       int graphic_2 = el2panelimg(element);
5698
5699       if (game.belt_dir[i] == MV_LEFT)
5700       {
5701         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5702         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5703       }
5704       else
5705       {
5706         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5707         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5708       }
5709     }
5710   }
5711
5712   SCAN_PLAYFIELD(x, y)
5713   {
5714     int element = Feld[x][y];
5715
5716     for (i = 0; i < NUM_BELTS; i++)
5717     {
5718       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5719       {
5720         int e_belt_nr = getBeltNrFromBeltElement(element);
5721         int belt_nr = i;
5722
5723         if (e_belt_nr == belt_nr)
5724         {
5725           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5726
5727           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5728         }
5729       }
5730     }
5731   }
5732 }
5733
5734 static void ToggleBeltSwitch(int x, int y)
5735 {
5736   static int belt_base_element[4] =
5737   {
5738     EL_CONVEYOR_BELT_1_LEFT,
5739     EL_CONVEYOR_BELT_2_LEFT,
5740     EL_CONVEYOR_BELT_3_LEFT,
5741     EL_CONVEYOR_BELT_4_LEFT
5742   };
5743   static int belt_base_active_element[4] =
5744   {
5745     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5746     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5747     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5748     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5749   };
5750   static int belt_base_switch_element[4] =
5751   {
5752     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5753     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5754     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5755     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5756   };
5757   static int belt_move_dir[4] =
5758   {
5759     MV_LEFT,
5760     MV_NONE,
5761     MV_RIGHT,
5762     MV_NONE,
5763   };
5764
5765   int element = Feld[x][y];
5766   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5767   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5768   int belt_dir = belt_move_dir[belt_dir_nr];
5769   int xx, yy, i;
5770
5771   if (!IS_BELT_SWITCH(element))
5772     return;
5773
5774   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5775   game.belt_dir[belt_nr] = belt_dir;
5776
5777   if (belt_dir_nr == 3)
5778     belt_dir_nr = 1;
5779
5780   /* set frame order for belt animation graphic according to belt direction */
5781   for (i = 0; i < NUM_BELT_PARTS; i++)
5782   {
5783     int element = belt_base_active_element[belt_nr] + i;
5784     int graphic_1 = el2img(element);
5785     int graphic_2 = el2panelimg(element);
5786
5787     if (belt_dir == MV_LEFT)
5788     {
5789       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5790       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5791     }
5792     else
5793     {
5794       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5795       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5796     }
5797   }
5798
5799   SCAN_PLAYFIELD(xx, yy)
5800   {
5801     int element = Feld[xx][yy];
5802
5803     if (IS_BELT_SWITCH(element))
5804     {
5805       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5806
5807       if (e_belt_nr == belt_nr)
5808       {
5809         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5810         DrawLevelField(xx, yy);
5811       }
5812     }
5813     else if (IS_BELT(element) && belt_dir != MV_NONE)
5814     {
5815       int e_belt_nr = getBeltNrFromBeltElement(element);
5816
5817       if (e_belt_nr == belt_nr)
5818       {
5819         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5820
5821         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5822         DrawLevelField(xx, yy);
5823       }
5824     }
5825     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5826     {
5827       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5828
5829       if (e_belt_nr == belt_nr)
5830       {
5831         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5832
5833         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5834         DrawLevelField(xx, yy);
5835       }
5836     }
5837   }
5838 }
5839
5840 static void ToggleSwitchgateSwitch(int x, int y)
5841 {
5842   int xx, yy;
5843
5844   game.switchgate_pos = !game.switchgate_pos;
5845
5846   SCAN_PLAYFIELD(xx, yy)
5847   {
5848     int element = Feld[xx][yy];
5849
5850 #if !USE_BOTH_SWITCHGATE_SWITCHES
5851     if (element == EL_SWITCHGATE_SWITCH_UP ||
5852         element == EL_SWITCHGATE_SWITCH_DOWN)
5853     {
5854       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5855       DrawLevelField(xx, yy);
5856     }
5857     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5858              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5859     {
5860       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5861       DrawLevelField(xx, yy);
5862     }
5863 #else
5864     if (element == EL_SWITCHGATE_SWITCH_UP)
5865     {
5866       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5867       DrawLevelField(xx, yy);
5868     }
5869     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5870     {
5871       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5872       DrawLevelField(xx, yy);
5873     }
5874     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5875     {
5876       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5877       DrawLevelField(xx, yy);
5878     }
5879     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5880     {
5881       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5882       DrawLevelField(xx, yy);
5883     }
5884 #endif
5885     else if (element == EL_SWITCHGATE_OPEN ||
5886              element == EL_SWITCHGATE_OPENING)
5887     {
5888       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5889
5890       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5891     }
5892     else if (element == EL_SWITCHGATE_CLOSED ||
5893              element == EL_SWITCHGATE_CLOSING)
5894     {
5895       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5896
5897       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5898     }
5899   }
5900 }
5901
5902 static int getInvisibleActiveFromInvisibleElement(int element)
5903 {
5904   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5905           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5906           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5907           element);
5908 }
5909
5910 static int getInvisibleFromInvisibleActiveElement(int element)
5911 {
5912   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5913           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5914           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5915           element);
5916 }
5917
5918 static void RedrawAllLightSwitchesAndInvisibleElements()
5919 {
5920   int x, y;
5921
5922   SCAN_PLAYFIELD(x, y)
5923   {
5924     int element = Feld[x][y];
5925
5926     if (element == EL_LIGHT_SWITCH &&
5927         game.light_time_left > 0)
5928     {
5929       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5930       DrawLevelField(x, y);
5931     }
5932     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5933              game.light_time_left == 0)
5934     {
5935       Feld[x][y] = EL_LIGHT_SWITCH;
5936       DrawLevelField(x, y);
5937     }
5938     else if (element == EL_EMC_DRIPPER &&
5939              game.light_time_left > 0)
5940     {
5941       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5942       DrawLevelField(x, y);
5943     }
5944     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5945              game.light_time_left == 0)
5946     {
5947       Feld[x][y] = EL_EMC_DRIPPER;
5948       DrawLevelField(x, y);
5949     }
5950     else if (element == EL_INVISIBLE_STEELWALL ||
5951              element == EL_INVISIBLE_WALL ||
5952              element == EL_INVISIBLE_SAND)
5953     {
5954       if (game.light_time_left > 0)
5955         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5956
5957       DrawLevelField(x, y);
5958
5959       /* uncrumble neighbour fields, if needed */
5960       if (element == EL_INVISIBLE_SAND)
5961         DrawLevelFieldCrumbledSandNeighbours(x, y);
5962     }
5963     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5964              element == EL_INVISIBLE_WALL_ACTIVE ||
5965              element == EL_INVISIBLE_SAND_ACTIVE)
5966     {
5967       if (game.light_time_left == 0)
5968         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5969
5970       DrawLevelField(x, y);
5971
5972       /* re-crumble neighbour fields, if needed */
5973       if (element == EL_INVISIBLE_SAND)
5974         DrawLevelFieldCrumbledSandNeighbours(x, y);
5975     }
5976   }
5977 }
5978
5979 static void RedrawAllInvisibleElementsForLenses()
5980 {
5981   int x, y;
5982
5983   SCAN_PLAYFIELD(x, y)
5984   {
5985     int element = Feld[x][y];
5986
5987     if (element == EL_EMC_DRIPPER &&
5988         game.lenses_time_left > 0)
5989     {
5990       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5991       DrawLevelField(x, y);
5992     }
5993     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5994              game.lenses_time_left == 0)
5995     {
5996       Feld[x][y] = EL_EMC_DRIPPER;
5997       DrawLevelField(x, y);
5998     }
5999     else if (element == EL_INVISIBLE_STEELWALL ||
6000              element == EL_INVISIBLE_WALL ||
6001              element == EL_INVISIBLE_SAND)
6002     {
6003       if (game.lenses_time_left > 0)
6004         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6005
6006       DrawLevelField(x, y);
6007
6008       /* uncrumble neighbour fields, if needed */
6009       if (element == EL_INVISIBLE_SAND)
6010         DrawLevelFieldCrumbledSandNeighbours(x, y);
6011     }
6012     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6013              element == EL_INVISIBLE_WALL_ACTIVE ||
6014              element == EL_INVISIBLE_SAND_ACTIVE)
6015     {
6016       if (game.lenses_time_left == 0)
6017         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6018
6019       DrawLevelField(x, y);
6020
6021       /* re-crumble neighbour fields, if needed */
6022       if (element == EL_INVISIBLE_SAND)
6023         DrawLevelFieldCrumbledSandNeighbours(x, y);
6024     }
6025   }
6026 }
6027
6028 static void RedrawAllInvisibleElementsForMagnifier()
6029 {
6030   int x, y;
6031
6032   SCAN_PLAYFIELD(x, y)
6033   {
6034     int element = Feld[x][y];
6035
6036     if (element == EL_EMC_FAKE_GRASS &&
6037         game.magnify_time_left > 0)
6038     {
6039       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6040       DrawLevelField(x, y);
6041     }
6042     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6043              game.magnify_time_left == 0)
6044     {
6045       Feld[x][y] = EL_EMC_FAKE_GRASS;
6046       DrawLevelField(x, y);
6047     }
6048     else if (IS_GATE_GRAY(element) &&
6049              game.magnify_time_left > 0)
6050     {
6051       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6052                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6053                     IS_EM_GATE_GRAY(element) ?
6054                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6055                     IS_EMC_GATE_GRAY(element) ?
6056                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6057                     element);
6058       DrawLevelField(x, y);
6059     }
6060     else if (IS_GATE_GRAY_ACTIVE(element) &&
6061              game.magnify_time_left == 0)
6062     {
6063       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6064                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6065                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6066                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6067                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6068                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6069                     element);
6070       DrawLevelField(x, y);
6071     }
6072   }
6073 }
6074
6075 static void ToggleLightSwitch(int x, int y)
6076 {
6077   int element = Feld[x][y];
6078
6079   game.light_time_left =
6080     (element == EL_LIGHT_SWITCH ?
6081      level.time_light * FRAMES_PER_SECOND : 0);
6082
6083   RedrawAllLightSwitchesAndInvisibleElements();
6084 }
6085
6086 static void ActivateTimegateSwitch(int x, int y)
6087 {
6088   int xx, yy;
6089
6090   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6091
6092   SCAN_PLAYFIELD(xx, yy)
6093   {
6094     int element = Feld[xx][yy];
6095
6096     if (element == EL_TIMEGATE_CLOSED ||
6097         element == EL_TIMEGATE_CLOSING)
6098     {
6099       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6100       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6101     }
6102
6103     /*
6104     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6105     {
6106       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6107       DrawLevelField(xx, yy);
6108     }
6109     */
6110
6111   }
6112
6113 #if 1
6114   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6115                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6116 #else
6117   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6118 #endif
6119 }
6120
6121 void Impact(int x, int y)
6122 {
6123   boolean last_line = (y == lev_fieldy - 1);
6124   boolean object_hit = FALSE;
6125   boolean impact = (last_line || object_hit);
6126   int element = Feld[x][y];
6127   int smashed = EL_STEELWALL;
6128
6129   if (!last_line)       /* check if element below was hit */
6130   {
6131     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6132       return;
6133
6134     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6135                                          MovDir[x][y + 1] != MV_DOWN ||
6136                                          MovPos[x][y + 1] <= TILEY / 2));
6137
6138     /* do not smash moving elements that left the smashed field in time */
6139     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6140         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6141       object_hit = FALSE;
6142
6143 #if USE_QUICKSAND_IMPACT_BUGFIX
6144     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6145     {
6146       RemoveMovingField(x, y + 1);
6147       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6148       Feld[x][y + 2] = EL_ROCK;
6149       DrawLevelField(x, y + 2);
6150
6151       object_hit = TRUE;
6152     }
6153
6154     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6155     {
6156       RemoveMovingField(x, y + 1);
6157       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6158       Feld[x][y + 2] = EL_ROCK;
6159       DrawLevelField(x, y + 2);
6160
6161       object_hit = TRUE;
6162     }
6163 #endif
6164
6165     if (object_hit)
6166       smashed = MovingOrBlocked2Element(x, y + 1);
6167
6168     impact = (last_line || object_hit);
6169   }
6170
6171   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6172   {
6173     SplashAcid(x, y + 1);
6174     return;
6175   }
6176
6177   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6178   /* only reset graphic animation if graphic really changes after impact */
6179   if (impact &&
6180       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6181   {
6182     ResetGfxAnimation(x, y);
6183     DrawLevelField(x, y);
6184   }
6185
6186   if (impact && CAN_EXPLODE_IMPACT(element))
6187   {
6188     Bang(x, y);
6189     return;
6190   }
6191   else if (impact && element == EL_PEARL &&
6192            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6193   {
6194     ResetGfxAnimation(x, y);
6195
6196     Feld[x][y] = EL_PEARL_BREAKING;
6197     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6198     return;
6199   }
6200   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6201   {
6202     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6203
6204     return;
6205   }
6206
6207   if (impact && element == EL_AMOEBA_DROP)
6208   {
6209     if (object_hit && IS_PLAYER(x, y + 1))
6210       KillPlayerUnlessEnemyProtected(x, y + 1);
6211     else if (object_hit && smashed == EL_PENGUIN)
6212       Bang(x, y + 1);
6213     else
6214     {
6215       Feld[x][y] = EL_AMOEBA_GROWING;
6216       Store[x][y] = EL_AMOEBA_WET;
6217
6218       ResetRandomAnimationValue(x, y);
6219     }
6220     return;
6221   }
6222
6223   if (object_hit)               /* check which object was hit */
6224   {
6225     if ((CAN_PASS_MAGIC_WALL(element) && 
6226          (smashed == EL_MAGIC_WALL ||
6227           smashed == EL_BD_MAGIC_WALL)) ||
6228         (CAN_PASS_DC_MAGIC_WALL(element) &&
6229          smashed == EL_DC_MAGIC_WALL))
6230     {
6231       int xx, yy;
6232       int activated_magic_wall =
6233         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6234          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6235          EL_DC_MAGIC_WALL_ACTIVE);
6236
6237       /* activate magic wall / mill */
6238       SCAN_PLAYFIELD(xx, yy)
6239       {
6240         if (Feld[xx][yy] == smashed)
6241           Feld[xx][yy] = activated_magic_wall;
6242       }
6243
6244       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6245       game.magic_wall_active = TRUE;
6246
6247       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6248                             SND_MAGIC_WALL_ACTIVATING :
6249                             smashed == EL_BD_MAGIC_WALL ?
6250                             SND_BD_MAGIC_WALL_ACTIVATING :
6251                             SND_DC_MAGIC_WALL_ACTIVATING));
6252     }
6253
6254     if (IS_PLAYER(x, y + 1))
6255     {
6256       if (CAN_SMASH_PLAYER(element))
6257       {
6258         KillPlayerUnlessEnemyProtected(x, y + 1);
6259         return;
6260       }
6261     }
6262     else if (smashed == EL_PENGUIN)
6263     {
6264       if (CAN_SMASH_PLAYER(element))
6265       {
6266         Bang(x, y + 1);
6267         return;
6268       }
6269     }
6270     else if (element == EL_BD_DIAMOND)
6271     {
6272       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6273       {
6274         Bang(x, y + 1);
6275         return;
6276       }
6277     }
6278     else if (((element == EL_SP_INFOTRON ||
6279                element == EL_SP_ZONK) &&
6280               (smashed == EL_SP_SNIKSNAK ||
6281                smashed == EL_SP_ELECTRON ||
6282                smashed == EL_SP_DISK_ORANGE)) ||
6283              (element == EL_SP_INFOTRON &&
6284               smashed == EL_SP_DISK_YELLOW))
6285     {
6286       Bang(x, y + 1);
6287       return;
6288     }
6289     else if (CAN_SMASH_EVERYTHING(element))
6290     {
6291       if (IS_CLASSIC_ENEMY(smashed) ||
6292           CAN_EXPLODE_SMASHED(smashed))
6293       {
6294         Bang(x, y + 1);
6295         return;
6296       }
6297       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6298       {
6299         if (smashed == EL_LAMP ||
6300             smashed == EL_LAMP_ACTIVE)
6301         {
6302           Bang(x, y + 1);
6303           return;
6304         }
6305         else if (smashed == EL_NUT)
6306         {
6307           Feld[x][y + 1] = EL_NUT_BREAKING;
6308           PlayLevelSound(x, y, SND_NUT_BREAKING);
6309           RaiseScoreElement(EL_NUT);
6310           return;
6311         }
6312         else if (smashed == EL_PEARL)
6313         {
6314           ResetGfxAnimation(x, y);
6315
6316           Feld[x][y + 1] = EL_PEARL_BREAKING;
6317           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6318           return;
6319         }
6320         else if (smashed == EL_DIAMOND)
6321         {
6322           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6323           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6324           return;
6325         }
6326         else if (IS_BELT_SWITCH(smashed))
6327         {
6328           ToggleBeltSwitch(x, y + 1);
6329         }
6330         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6331                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6332                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6333                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6334         {
6335           ToggleSwitchgateSwitch(x, y + 1);
6336         }
6337         else if (smashed == EL_LIGHT_SWITCH ||
6338                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6339         {
6340           ToggleLightSwitch(x, y + 1);
6341         }
6342         else
6343         {
6344 #if 0
6345           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6346 #endif
6347
6348           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6349
6350           CheckElementChangeBySide(x, y + 1, smashed, element,
6351                                    CE_SWITCHED, CH_SIDE_TOP);
6352           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6353                                             CH_SIDE_TOP);
6354         }
6355       }
6356       else
6357       {
6358         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6359       }
6360     }
6361   }
6362
6363   /* play sound of magic wall / mill */
6364   if (!last_line &&
6365       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6366        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6367        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6368   {
6369     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6370       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6371     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6372       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6373     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6374       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6375
6376     return;
6377   }
6378
6379   /* play sound of object that hits the ground */
6380   if (last_line || object_hit)
6381     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6382 }
6383
6384 inline static void TurnRoundExt(int x, int y)
6385 {
6386   static struct
6387   {
6388     int dx, dy;
6389   } move_xy[] =
6390   {
6391     {  0,  0 },
6392     { -1,  0 },
6393     { +1,  0 },
6394     {  0,  0 },
6395     {  0, -1 },
6396     {  0,  0 }, { 0, 0 }, { 0, 0 },
6397     {  0, +1 }
6398   };
6399   static struct
6400   {
6401     int left, right, back;
6402   } turn[] =
6403   {
6404     { 0,        0,              0        },
6405     { MV_DOWN,  MV_UP,          MV_RIGHT },
6406     { MV_UP,    MV_DOWN,        MV_LEFT  },
6407     { 0,        0,              0        },
6408     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6409     { 0,        0,              0        },
6410     { 0,        0,              0        },
6411     { 0,        0,              0        },
6412     { MV_RIGHT, MV_LEFT,        MV_UP    }
6413   };
6414
6415   int element = Feld[x][y];
6416   int move_pattern = element_info[element].move_pattern;
6417
6418   int old_move_dir = MovDir[x][y];
6419   int left_dir  = turn[old_move_dir].left;
6420   int right_dir = turn[old_move_dir].right;
6421   int back_dir  = turn[old_move_dir].back;
6422
6423   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6424   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6425   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6426   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6427
6428   int left_x  = x + left_dx,  left_y  = y + left_dy;
6429   int right_x = x + right_dx, right_y = y + right_dy;
6430   int move_x  = x + move_dx,  move_y  = y + move_dy;
6431
6432   int xx, yy;
6433
6434   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6435   {
6436     TestIfBadThingTouchesOtherBadThing(x, y);
6437
6438     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6439       MovDir[x][y] = right_dir;
6440     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6441       MovDir[x][y] = left_dir;
6442
6443     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6444       MovDelay[x][y] = 9;
6445     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6446       MovDelay[x][y] = 1;
6447   }
6448   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6449   {
6450     TestIfBadThingTouchesOtherBadThing(x, y);
6451
6452     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6453       MovDir[x][y] = left_dir;
6454     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6455       MovDir[x][y] = right_dir;
6456
6457     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6458       MovDelay[x][y] = 9;
6459     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6460       MovDelay[x][y] = 1;
6461   }
6462   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6463   {
6464     TestIfBadThingTouchesOtherBadThing(x, y);
6465
6466     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6467       MovDir[x][y] = left_dir;
6468     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6469       MovDir[x][y] = right_dir;
6470
6471     if (MovDir[x][y] != old_move_dir)
6472       MovDelay[x][y] = 9;
6473   }
6474   else if (element == EL_YAMYAM)
6475   {
6476     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6477     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6478
6479     if (can_turn_left && can_turn_right)
6480       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6481     else if (can_turn_left)
6482       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6483     else if (can_turn_right)
6484       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6485     else
6486       MovDir[x][y] = back_dir;
6487
6488     MovDelay[x][y] = 16 + 16 * RND(3);
6489   }
6490   else if (element == EL_DARK_YAMYAM)
6491   {
6492     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6493                                                          left_x, left_y);
6494     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6495                                                          right_x, right_y);
6496
6497     if (can_turn_left && can_turn_right)
6498       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6499     else if (can_turn_left)
6500       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6501     else if (can_turn_right)
6502       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6503     else
6504       MovDir[x][y] = back_dir;
6505
6506     MovDelay[x][y] = 16 + 16 * RND(3);
6507   }
6508   else if (element == EL_PACMAN)
6509   {
6510     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6511     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6512
6513     if (can_turn_left && can_turn_right)
6514       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6515     else if (can_turn_left)
6516       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6517     else if (can_turn_right)
6518       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6519     else
6520       MovDir[x][y] = back_dir;
6521
6522     MovDelay[x][y] = 6 + RND(40);
6523   }
6524   else if (element == EL_PIG)
6525   {
6526     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6527     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6528     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6529     boolean should_turn_left, should_turn_right, should_move_on;
6530     int rnd_value = 24;
6531     int rnd = RND(rnd_value);
6532
6533     should_turn_left = (can_turn_left &&
6534                         (!can_move_on ||
6535                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6536                                                    y + back_dy + left_dy)));
6537     should_turn_right = (can_turn_right &&
6538                          (!can_move_on ||
6539                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6540                                                     y + back_dy + right_dy)));
6541     should_move_on = (can_move_on &&
6542                       (!can_turn_left ||
6543                        !can_turn_right ||
6544                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6545                                                  y + move_dy + left_dy) ||
6546                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6547                                                  y + move_dy + right_dy)));
6548
6549     if (should_turn_left || should_turn_right || should_move_on)
6550     {
6551       if (should_turn_left && should_turn_right && should_move_on)
6552         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6553                         rnd < 2 * rnd_value / 3 ? right_dir :
6554                         old_move_dir);
6555       else if (should_turn_left && should_turn_right)
6556         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6557       else if (should_turn_left && should_move_on)
6558         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6559       else if (should_turn_right && should_move_on)
6560         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6561       else if (should_turn_left)
6562         MovDir[x][y] = left_dir;
6563       else if (should_turn_right)
6564         MovDir[x][y] = right_dir;
6565       else if (should_move_on)
6566         MovDir[x][y] = old_move_dir;
6567     }
6568     else if (can_move_on && rnd > rnd_value / 8)
6569       MovDir[x][y] = old_move_dir;
6570     else if (can_turn_left && can_turn_right)
6571       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6572     else if (can_turn_left && rnd > rnd_value / 8)
6573       MovDir[x][y] = left_dir;
6574     else if (can_turn_right && rnd > rnd_value/8)
6575       MovDir[x][y] = right_dir;
6576     else
6577       MovDir[x][y] = back_dir;
6578
6579     xx = x + move_xy[MovDir[x][y]].dx;
6580     yy = y + move_xy[MovDir[x][y]].dy;
6581
6582     if (!IN_LEV_FIELD(xx, yy) ||
6583         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6584       MovDir[x][y] = old_move_dir;
6585
6586     MovDelay[x][y] = 0;
6587   }
6588   else if (element == EL_DRAGON)
6589   {
6590     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6591     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6592     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6593     int rnd_value = 24;
6594     int rnd = RND(rnd_value);
6595
6596     if (can_move_on && rnd > rnd_value / 8)
6597       MovDir[x][y] = old_move_dir;
6598     else if (can_turn_left && can_turn_right)
6599       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6600     else if (can_turn_left && rnd > rnd_value / 8)
6601       MovDir[x][y] = left_dir;
6602     else if (can_turn_right && rnd > rnd_value / 8)
6603       MovDir[x][y] = right_dir;
6604     else
6605       MovDir[x][y] = back_dir;
6606
6607     xx = x + move_xy[MovDir[x][y]].dx;
6608     yy = y + move_xy[MovDir[x][y]].dy;
6609
6610     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6611       MovDir[x][y] = old_move_dir;
6612
6613     MovDelay[x][y] = 0;
6614   }
6615   else if (element == EL_MOLE)
6616   {
6617     boolean can_move_on =
6618       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6619                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6620                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6621     if (!can_move_on)
6622     {
6623       boolean can_turn_left =
6624         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6625                               IS_AMOEBOID(Feld[left_x][left_y])));
6626
6627       boolean can_turn_right =
6628         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6629                               IS_AMOEBOID(Feld[right_x][right_y])));
6630
6631       if (can_turn_left && can_turn_right)
6632         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6633       else if (can_turn_left)
6634         MovDir[x][y] = left_dir;
6635       else
6636         MovDir[x][y] = right_dir;
6637     }
6638
6639     if (MovDir[x][y] != old_move_dir)
6640       MovDelay[x][y] = 9;
6641   }
6642   else if (element == EL_BALLOON)
6643   {
6644     MovDir[x][y] = game.wind_direction;
6645     MovDelay[x][y] = 0;
6646   }
6647   else if (element == EL_SPRING)
6648   {
6649 #if USE_NEW_SPRING_BUMPER
6650     if (MovDir[x][y] & MV_HORIZONTAL)
6651     {
6652       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6653           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6654       {
6655         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6656         ResetGfxAnimation(move_x, move_y);
6657         DrawLevelField(move_x, move_y);
6658
6659         MovDir[x][y] = back_dir;
6660       }
6661       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6662                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6663         MovDir[x][y] = MV_NONE;
6664     }
6665 #else
6666     if (MovDir[x][y] & MV_HORIZONTAL &&
6667         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6668          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6669       MovDir[x][y] = MV_NONE;
6670 #endif
6671
6672     MovDelay[x][y] = 0;
6673   }
6674   else if (element == EL_ROBOT ||
6675            element == EL_SATELLITE ||
6676            element == EL_PENGUIN ||
6677            element == EL_EMC_ANDROID)
6678   {
6679     int attr_x = -1, attr_y = -1;
6680
6681     if (AllPlayersGone)
6682     {
6683       attr_x = ExitX;
6684       attr_y = ExitY;
6685     }
6686     else
6687     {
6688       int i;
6689
6690       for (i = 0; i < MAX_PLAYERS; i++)
6691       {
6692         struct PlayerInfo *player = &stored_player[i];
6693         int jx = player->jx, jy = player->jy;
6694
6695         if (!player->active)
6696           continue;
6697
6698         if (attr_x == -1 ||
6699             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6700         {
6701           attr_x = jx;
6702           attr_y = jy;
6703         }
6704       }
6705     }
6706
6707     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6708         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6709          game.engine_version < VERSION_IDENT(3,1,0,0)))
6710     {
6711       attr_x = ZX;
6712       attr_y = ZY;
6713     }
6714
6715     if (element == EL_PENGUIN)
6716     {
6717       int i;
6718       static int xy[4][2] =
6719       {
6720         { 0, -1 },
6721         { -1, 0 },
6722         { +1, 0 },
6723         { 0, +1 }
6724       };
6725
6726       for (i = 0; i < NUM_DIRECTIONS; i++)
6727       {
6728         int ex = x + xy[i][0];
6729         int ey = y + xy[i][1];
6730
6731         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6732                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6733                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6734                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6735         {
6736           attr_x = ex;
6737           attr_y = ey;
6738           break;
6739         }
6740       }
6741     }
6742
6743     MovDir[x][y] = MV_NONE;
6744     if (attr_x < x)
6745       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6746     else if (attr_x > x)
6747       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6748     if (attr_y < y)
6749       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6750     else if (attr_y > y)
6751       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6752
6753     if (element == EL_ROBOT)
6754     {
6755       int newx, newy;
6756
6757       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6758         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6759       Moving2Blocked(x, y, &newx, &newy);
6760
6761       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6762         MovDelay[x][y] = 8 + 8 * !RND(3);
6763       else
6764         MovDelay[x][y] = 16;
6765     }
6766     else if (element == EL_PENGUIN)
6767     {
6768       int newx, newy;
6769
6770       MovDelay[x][y] = 1;
6771
6772       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6773       {
6774         boolean first_horiz = RND(2);
6775         int new_move_dir = MovDir[x][y];
6776
6777         MovDir[x][y] =
6778           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6779         Moving2Blocked(x, y, &newx, &newy);
6780
6781         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6782           return;
6783
6784         MovDir[x][y] =
6785           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6786         Moving2Blocked(x, y, &newx, &newy);
6787
6788         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6789           return;
6790
6791         MovDir[x][y] = old_move_dir;
6792         return;
6793       }
6794     }
6795     else if (element == EL_SATELLITE)
6796     {
6797       int newx, newy;
6798
6799       MovDelay[x][y] = 1;
6800
6801       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6802       {
6803         boolean first_horiz = RND(2);
6804         int new_move_dir = MovDir[x][y];
6805
6806         MovDir[x][y] =
6807           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6808         Moving2Blocked(x, y, &newx, &newy);
6809
6810         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6811           return;
6812
6813         MovDir[x][y] =
6814           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6815         Moving2Blocked(x, y, &newx, &newy);
6816
6817         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6818           return;
6819
6820         MovDir[x][y] = old_move_dir;
6821         return;
6822       }
6823     }
6824     else if (element == EL_EMC_ANDROID)
6825     {
6826       static int check_pos[16] =
6827       {
6828         -1,             /*  0 => (invalid)          */
6829         7,              /*  1 => MV_LEFT            */
6830         3,              /*  2 => MV_RIGHT           */
6831         -1,             /*  3 => (invalid)          */
6832         1,              /*  4 =>            MV_UP   */
6833         0,              /*  5 => MV_LEFT  | MV_UP   */
6834         2,              /*  6 => MV_RIGHT | MV_UP   */
6835         -1,             /*  7 => (invalid)          */
6836         5,              /*  8 =>            MV_DOWN */
6837         6,              /*  9 => MV_LEFT  | MV_DOWN */
6838         4,              /* 10 => MV_RIGHT | MV_DOWN */
6839         -1,             /* 11 => (invalid)          */
6840         -1,             /* 12 => (invalid)          */
6841         -1,             /* 13 => (invalid)          */
6842         -1,             /* 14 => (invalid)          */
6843         -1,             /* 15 => (invalid)          */
6844       };
6845       static struct
6846       {
6847         int dx, dy;
6848         int dir;
6849       } check_xy[8] =
6850       {
6851         { -1, -1,       MV_LEFT  | MV_UP   },
6852         {  0, -1,                  MV_UP   },
6853         { +1, -1,       MV_RIGHT | MV_UP   },
6854         { +1,  0,       MV_RIGHT           },
6855         { +1, +1,       MV_RIGHT | MV_DOWN },
6856         {  0, +1,                  MV_DOWN },
6857         { -1, +1,       MV_LEFT  | MV_DOWN },
6858         { -1,  0,       MV_LEFT            },
6859       };
6860       int start_pos, check_order;
6861       boolean can_clone = FALSE;
6862       int i;
6863
6864       /* check if there is any free field around current position */
6865       for (i = 0; i < 8; i++)
6866       {
6867         int newx = x + check_xy[i].dx;
6868         int newy = y + check_xy[i].dy;
6869
6870         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6871         {
6872           can_clone = TRUE;
6873
6874           break;
6875         }
6876       }
6877
6878       if (can_clone)            /* randomly find an element to clone */
6879       {
6880         can_clone = FALSE;
6881
6882         start_pos = check_pos[RND(8)];
6883         check_order = (RND(2) ? -1 : +1);
6884
6885         for (i = 0; i < 8; i++)
6886         {
6887           int pos_raw = start_pos + i * check_order;
6888           int pos = (pos_raw + 8) % 8;
6889           int newx = x + check_xy[pos].dx;
6890           int newy = y + check_xy[pos].dy;
6891
6892           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6893           {
6894             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6895             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6896
6897             Store[x][y] = Feld[newx][newy];
6898
6899             can_clone = TRUE;
6900
6901             break;
6902           }
6903         }
6904       }
6905
6906       if (can_clone)            /* randomly find a direction to move */
6907       {
6908         can_clone = FALSE;
6909
6910         start_pos = check_pos[RND(8)];
6911         check_order = (RND(2) ? -1 : +1);
6912
6913         for (i = 0; i < 8; i++)
6914         {
6915           int pos_raw = start_pos + i * check_order;
6916           int pos = (pos_raw + 8) % 8;
6917           int newx = x + check_xy[pos].dx;
6918           int newy = y + check_xy[pos].dy;
6919           int new_move_dir = check_xy[pos].dir;
6920
6921           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6922           {
6923             MovDir[x][y] = new_move_dir;
6924             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6925
6926             can_clone = TRUE;
6927
6928             break;
6929           }
6930         }
6931       }
6932
6933       if (can_clone)            /* cloning and moving successful */
6934         return;
6935
6936       /* cannot clone -- try to move towards player */
6937
6938       start_pos = check_pos[MovDir[x][y] & 0x0f];
6939       check_order = (RND(2) ? -1 : +1);
6940
6941       for (i = 0; i < 3; i++)
6942       {
6943         /* first check start_pos, then previous/next or (next/previous) pos */
6944         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6945         int pos = (pos_raw + 8) % 8;
6946         int newx = x + check_xy[pos].dx;
6947         int newy = y + check_xy[pos].dy;
6948         int new_move_dir = check_xy[pos].dir;
6949
6950         if (IS_PLAYER(newx, newy))
6951           break;
6952
6953         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6954         {
6955           MovDir[x][y] = new_move_dir;
6956           MovDelay[x][y] = level.android_move_time * 8 + 1;
6957
6958           break;
6959         }
6960       }
6961     }
6962   }
6963   else if (move_pattern == MV_TURNING_LEFT ||
6964            move_pattern == MV_TURNING_RIGHT ||
6965            move_pattern == MV_TURNING_LEFT_RIGHT ||
6966            move_pattern == MV_TURNING_RIGHT_LEFT ||
6967            move_pattern == MV_TURNING_RANDOM ||
6968            move_pattern == MV_ALL_DIRECTIONS)
6969   {
6970     boolean can_turn_left =
6971       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6972     boolean can_turn_right =
6973       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6974
6975     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6976       return;
6977
6978     if (move_pattern == MV_TURNING_LEFT)
6979       MovDir[x][y] = left_dir;
6980     else if (move_pattern == MV_TURNING_RIGHT)
6981       MovDir[x][y] = right_dir;
6982     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6983       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6984     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6985       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6986     else if (move_pattern == MV_TURNING_RANDOM)
6987       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6988                       can_turn_right && !can_turn_left ? right_dir :
6989                       RND(2) ? left_dir : right_dir);
6990     else if (can_turn_left && can_turn_right)
6991       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6992     else if (can_turn_left)
6993       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6994     else if (can_turn_right)
6995       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6996     else
6997       MovDir[x][y] = back_dir;
6998
6999     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7000   }
7001   else if (move_pattern == MV_HORIZONTAL ||
7002            move_pattern == MV_VERTICAL)
7003   {
7004     if (move_pattern & old_move_dir)
7005       MovDir[x][y] = back_dir;
7006     else if (move_pattern == MV_HORIZONTAL)
7007       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7008     else if (move_pattern == MV_VERTICAL)
7009       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7010
7011     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7012   }
7013   else if (move_pattern & MV_ANY_DIRECTION)
7014   {
7015     MovDir[x][y] = move_pattern;
7016     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7017   }
7018   else if (move_pattern & MV_WIND_DIRECTION)
7019   {
7020     MovDir[x][y] = game.wind_direction;
7021     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7022   }
7023   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7024   {
7025     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7026       MovDir[x][y] = left_dir;
7027     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7028       MovDir[x][y] = right_dir;
7029
7030     if (MovDir[x][y] != old_move_dir)
7031       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7032   }
7033   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7034   {
7035     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7036       MovDir[x][y] = right_dir;
7037     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7038       MovDir[x][y] = left_dir;
7039
7040     if (MovDir[x][y] != old_move_dir)
7041       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7042   }
7043   else if (move_pattern == MV_TOWARDS_PLAYER ||
7044            move_pattern == MV_AWAY_FROM_PLAYER)
7045   {
7046     int attr_x = -1, attr_y = -1;
7047     int newx, newy;
7048     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7049
7050     if (AllPlayersGone)
7051     {
7052       attr_x = ExitX;
7053       attr_y = ExitY;
7054     }
7055     else
7056     {
7057       int i;
7058
7059       for (i = 0; i < MAX_PLAYERS; i++)
7060       {
7061         struct PlayerInfo *player = &stored_player[i];
7062         int jx = player->jx, jy = player->jy;
7063
7064         if (!player->active)
7065           continue;
7066
7067         if (attr_x == -1 ||
7068             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7069         {
7070           attr_x = jx;
7071           attr_y = jy;
7072         }
7073       }
7074     }
7075
7076     MovDir[x][y] = MV_NONE;
7077     if (attr_x < x)
7078       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7079     else if (attr_x > x)
7080       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7081     if (attr_y < y)
7082       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7083     else if (attr_y > y)
7084       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7085
7086     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7087
7088     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7089     {
7090       boolean first_horiz = RND(2);
7091       int new_move_dir = MovDir[x][y];
7092
7093       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7094       {
7095         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7096         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7097
7098         return;
7099       }
7100
7101       MovDir[x][y] =
7102         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7103       Moving2Blocked(x, y, &newx, &newy);
7104
7105       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7106         return;
7107
7108       MovDir[x][y] =
7109         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7110       Moving2Blocked(x, y, &newx, &newy);
7111
7112       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7113         return;
7114
7115       MovDir[x][y] = old_move_dir;
7116     }
7117   }
7118   else if (move_pattern == MV_WHEN_PUSHED ||
7119            move_pattern == MV_WHEN_DROPPED)
7120   {
7121     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7122       MovDir[x][y] = MV_NONE;
7123
7124     MovDelay[x][y] = 0;
7125   }
7126   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7127   {
7128     static int test_xy[7][2] =
7129     {
7130       { 0, -1 },
7131       { -1, 0 },
7132       { +1, 0 },
7133       { 0, +1 },
7134       { 0, -1 },
7135       { -1, 0 },
7136       { +1, 0 },
7137     };
7138     static int test_dir[7] =
7139     {
7140       MV_UP,
7141       MV_LEFT,
7142       MV_RIGHT,
7143       MV_DOWN,
7144       MV_UP,
7145       MV_LEFT,
7146       MV_RIGHT,
7147     };
7148     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7149     int move_preference = -1000000;     /* start with very low preference */
7150     int new_move_dir = MV_NONE;
7151     int start_test = RND(4);
7152     int i;
7153
7154     for (i = 0; i < NUM_DIRECTIONS; i++)
7155     {
7156       int move_dir = test_dir[start_test + i];
7157       int move_dir_preference;
7158
7159       xx = x + test_xy[start_test + i][0];
7160       yy = y + test_xy[start_test + i][1];
7161
7162       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7163           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7164       {
7165         new_move_dir = move_dir;
7166
7167         break;
7168       }
7169
7170       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7171         continue;
7172
7173       move_dir_preference = -1 * RunnerVisit[xx][yy];
7174       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7175         move_dir_preference = PlayerVisit[xx][yy];
7176
7177       if (move_dir_preference > move_preference)
7178       {
7179         /* prefer field that has not been visited for the longest time */
7180         move_preference = move_dir_preference;
7181         new_move_dir = move_dir;
7182       }
7183       else if (move_dir_preference == move_preference &&
7184                move_dir == old_move_dir)
7185       {
7186         /* prefer last direction when all directions are preferred equally */
7187         move_preference = move_dir_preference;
7188         new_move_dir = move_dir;
7189       }
7190     }
7191
7192     MovDir[x][y] = new_move_dir;
7193     if (old_move_dir != new_move_dir)
7194       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7195   }
7196 }
7197
7198 static void TurnRound(int x, int y)
7199 {
7200   int direction = MovDir[x][y];
7201
7202   TurnRoundExt(x, y);
7203
7204   GfxDir[x][y] = MovDir[x][y];
7205
7206   if (direction != MovDir[x][y])
7207     GfxFrame[x][y] = 0;
7208
7209   if (MovDelay[x][y])
7210     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7211
7212   ResetGfxFrame(x, y, FALSE);
7213 }
7214
7215 static boolean JustBeingPushed(int x, int y)
7216 {
7217   int i;
7218
7219   for (i = 0; i < MAX_PLAYERS; i++)
7220   {
7221     struct PlayerInfo *player = &stored_player[i];
7222
7223     if (player->active && player->is_pushing && player->MovPos)
7224     {
7225       int next_jx = player->jx + (player->jx - player->last_jx);
7226       int next_jy = player->jy + (player->jy - player->last_jy);
7227
7228       if (x == next_jx && y == next_jy)
7229         return TRUE;
7230     }
7231   }
7232
7233   return FALSE;
7234 }
7235
7236 void StartMoving(int x, int y)
7237 {
7238   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7239   int element = Feld[x][y];
7240
7241   if (Stop[x][y])
7242     return;
7243
7244   if (MovDelay[x][y] == 0)
7245     GfxAction[x][y] = ACTION_DEFAULT;
7246
7247   if (CAN_FALL(element) && y < lev_fieldy - 1)
7248   {
7249     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7250         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7251       if (JustBeingPushed(x, y))
7252         return;
7253
7254     if (element == EL_QUICKSAND_FULL)
7255     {
7256       if (IS_FREE(x, y + 1))
7257       {
7258         InitMovingField(x, y, MV_DOWN);
7259         started_moving = TRUE;
7260
7261         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7262 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7263         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7264           Store[x][y] = EL_ROCK;
7265 #else
7266         Store[x][y] = EL_ROCK;
7267 #endif
7268
7269         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7270       }
7271       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7272       {
7273         if (!MovDelay[x][y])
7274           MovDelay[x][y] = TILEY + 1;
7275
7276         if (MovDelay[x][y])
7277         {
7278           MovDelay[x][y]--;
7279           if (MovDelay[x][y])
7280             return;
7281         }
7282
7283         Feld[x][y] = EL_QUICKSAND_EMPTY;
7284         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7285         Store[x][y + 1] = Store[x][y];
7286         Store[x][y] = 0;
7287
7288         PlayLevelSoundAction(x, y, ACTION_FILLING);
7289       }
7290     }
7291     else if (element == EL_QUICKSAND_FAST_FULL)
7292     {
7293       if (IS_FREE(x, y + 1))
7294       {
7295         InitMovingField(x, y, MV_DOWN);
7296         started_moving = TRUE;
7297
7298         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7299 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7300         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7301           Store[x][y] = EL_ROCK;
7302 #else
7303         Store[x][y] = EL_ROCK;
7304 #endif
7305
7306         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7307       }
7308       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7309       {
7310         if (!MovDelay[x][y])
7311           MovDelay[x][y] = TILEY + 1;
7312
7313         if (MovDelay[x][y])
7314         {
7315           MovDelay[x][y]--;
7316           if (MovDelay[x][y])
7317             return;
7318         }
7319
7320         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7321         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7322         Store[x][y + 1] = Store[x][y];
7323         Store[x][y] = 0;
7324
7325         PlayLevelSoundAction(x, y, ACTION_FILLING);
7326       }
7327     }
7328     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7329              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7330     {
7331       InitMovingField(x, y, MV_DOWN);
7332       started_moving = TRUE;
7333
7334       Feld[x][y] = EL_QUICKSAND_FILLING;
7335       Store[x][y] = element;
7336
7337       PlayLevelSoundAction(x, y, ACTION_FILLING);
7338     }
7339     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7340              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7341     {
7342       InitMovingField(x, y, MV_DOWN);
7343       started_moving = TRUE;
7344
7345       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7346       Store[x][y] = element;
7347
7348       PlayLevelSoundAction(x, y, ACTION_FILLING);
7349     }
7350     else if (element == EL_MAGIC_WALL_FULL)
7351     {
7352       if (IS_FREE(x, y + 1))
7353       {
7354         InitMovingField(x, y, MV_DOWN);
7355         started_moving = TRUE;
7356
7357         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7358         Store[x][y] = EL_CHANGED(Store[x][y]);
7359       }
7360       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7361       {
7362         if (!MovDelay[x][y])
7363           MovDelay[x][y] = TILEY/4 + 1;
7364
7365         if (MovDelay[x][y])
7366         {
7367           MovDelay[x][y]--;
7368           if (MovDelay[x][y])
7369             return;
7370         }
7371
7372         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7373         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7374         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7375         Store[x][y] = 0;
7376       }
7377     }
7378     else if (element == EL_BD_MAGIC_WALL_FULL)
7379     {
7380       if (IS_FREE(x, y + 1))
7381       {
7382         InitMovingField(x, y, MV_DOWN);
7383         started_moving = TRUE;
7384
7385         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7386         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7387       }
7388       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7389       {
7390         if (!MovDelay[x][y])
7391           MovDelay[x][y] = TILEY/4 + 1;
7392
7393         if (MovDelay[x][y])
7394         {
7395           MovDelay[x][y]--;
7396           if (MovDelay[x][y])
7397             return;
7398         }
7399
7400         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7401         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7402         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7403         Store[x][y] = 0;
7404       }
7405     }
7406     else if (element == EL_DC_MAGIC_WALL_FULL)
7407     {
7408       if (IS_FREE(x, y + 1))
7409       {
7410         InitMovingField(x, y, MV_DOWN);
7411         started_moving = TRUE;
7412
7413         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7414         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7415       }
7416       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7417       {
7418         if (!MovDelay[x][y])
7419           MovDelay[x][y] = TILEY/4 + 1;
7420
7421         if (MovDelay[x][y])
7422         {
7423           MovDelay[x][y]--;
7424           if (MovDelay[x][y])
7425             return;
7426         }
7427
7428         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7429         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7430         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7431         Store[x][y] = 0;
7432       }
7433     }
7434     else if ((CAN_PASS_MAGIC_WALL(element) &&
7435               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7436                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7437              (CAN_PASS_DC_MAGIC_WALL(element) &&
7438               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7439
7440     {
7441       InitMovingField(x, y, MV_DOWN);
7442       started_moving = TRUE;
7443
7444       Feld[x][y] =
7445         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7446          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7447          EL_DC_MAGIC_WALL_FILLING);
7448       Store[x][y] = element;
7449     }
7450     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7451     {
7452       SplashAcid(x, y + 1);
7453
7454       InitMovingField(x, y, MV_DOWN);
7455       started_moving = TRUE;
7456
7457       Store[x][y] = EL_ACID;
7458     }
7459     else if (
7460 #if USE_FIX_IMPACT_COLLISION
7461              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7462               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7463 #else
7464              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7465               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7466 #endif
7467              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7468               CAN_FALL(element) && WasJustFalling[x][y] &&
7469               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7470
7471              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7472               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7473               (Feld[x][y + 1] == EL_BLOCKED)))
7474     {
7475       /* this is needed for a special case not covered by calling "Impact()"
7476          from "ContinueMoving()": if an element moves to a tile directly below
7477          another element which was just falling on that tile (which was empty
7478          in the previous frame), the falling element above would just stop
7479          instead of smashing the element below (in previous version, the above
7480          element was just checked for "moving" instead of "falling", resulting
7481          in incorrect smashes caused by horizontal movement of the above
7482          element; also, the case of the player being the element to smash was
7483          simply not covered here... :-/ ) */
7484
7485       CheckCollision[x][y] = 0;
7486       CheckImpact[x][y] = 0;
7487
7488       Impact(x, y);
7489     }
7490     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7491     {
7492       if (MovDir[x][y] == MV_NONE)
7493       {
7494         InitMovingField(x, y, MV_DOWN);
7495         started_moving = TRUE;
7496       }
7497     }
7498     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7499     {
7500       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7501         MovDir[x][y] = MV_DOWN;
7502
7503       InitMovingField(x, y, MV_DOWN);
7504       started_moving = TRUE;
7505     }
7506     else if (element == EL_AMOEBA_DROP)
7507     {
7508       Feld[x][y] = EL_AMOEBA_GROWING;
7509       Store[x][y] = EL_AMOEBA_WET;
7510     }
7511     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7512               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7513              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7514              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7515     {
7516       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7517                                 (IS_FREE(x - 1, y + 1) ||
7518                                  Feld[x - 1][y + 1] == EL_ACID));
7519       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7520                                 (IS_FREE(x + 1, y + 1) ||
7521                                  Feld[x + 1][y + 1] == EL_ACID));
7522       boolean can_fall_any  = (can_fall_left || can_fall_right);
7523       boolean can_fall_both = (can_fall_left && can_fall_right);
7524       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7525
7526 #if USE_NEW_ALL_SLIPPERY
7527       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7528       {
7529         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7530           can_fall_right = FALSE;
7531         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7532           can_fall_left = FALSE;
7533         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7534           can_fall_right = FALSE;
7535         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7536           can_fall_left = FALSE;
7537
7538         can_fall_any  = (can_fall_left || can_fall_right);
7539         can_fall_both = FALSE;
7540       }
7541 #else
7542       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7543       {
7544         if (slippery_type == SLIPPERY_ONLY_LEFT)
7545           can_fall_right = FALSE;
7546         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7547           can_fall_left = FALSE;
7548         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7549           can_fall_right = FALSE;
7550         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7551           can_fall_left = FALSE;
7552
7553         can_fall_any  = (can_fall_left || can_fall_right);
7554         can_fall_both = (can_fall_left && can_fall_right);
7555       }
7556 #endif
7557
7558 #if USE_NEW_ALL_SLIPPERY
7559 #else
7560 #if USE_NEW_SP_SLIPPERY
7561       /* !!! better use the same properties as for custom elements here !!! */
7562       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7563                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7564       {
7565         can_fall_right = FALSE;         /* slip down on left side */
7566         can_fall_both = FALSE;
7567       }
7568 #endif
7569 #endif
7570
7571 #if USE_NEW_ALL_SLIPPERY
7572       if (can_fall_both)
7573       {
7574         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7575           can_fall_right = FALSE;       /* slip down on left side */
7576         else
7577           can_fall_left = !(can_fall_right = RND(2));
7578
7579         can_fall_both = FALSE;
7580       }
7581 #else
7582       if (can_fall_both)
7583       {
7584         if (game.emulation == EMU_BOULDERDASH ||
7585             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7586           can_fall_right = FALSE;       /* slip down on left side */
7587         else
7588           can_fall_left = !(can_fall_right = RND(2));
7589
7590         can_fall_both = FALSE;
7591       }
7592 #endif
7593
7594       if (can_fall_any)
7595       {
7596         /* if not determined otherwise, prefer left side for slipping down */
7597         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7598         started_moving = TRUE;
7599       }
7600     }
7601 #if 0
7602     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7603 #else
7604     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7605 #endif
7606     {
7607       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7608       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7609       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7610       int belt_dir = game.belt_dir[belt_nr];
7611
7612       if ((belt_dir == MV_LEFT  && left_is_free) ||
7613           (belt_dir == MV_RIGHT && right_is_free))
7614       {
7615         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7616
7617         InitMovingField(x, y, belt_dir);
7618         started_moving = TRUE;
7619
7620         Pushed[x][y] = TRUE;
7621         Pushed[nextx][y] = TRUE;
7622
7623         GfxAction[x][y] = ACTION_DEFAULT;
7624       }
7625       else
7626       {
7627         MovDir[x][y] = 0;       /* if element was moving, stop it */
7628       }
7629     }
7630   }
7631
7632   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7633 #if 0
7634   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7635 #else
7636   if (CAN_MOVE(element) && !started_moving)
7637 #endif
7638   {
7639     int move_pattern = element_info[element].move_pattern;
7640     int newx, newy;
7641
7642 #if 0
7643 #if DEBUG
7644     if (MovDir[x][y] == MV_NONE)
7645     {
7646       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7647              x, y, element, element_info[element].token_name);
7648       printf("StartMoving(): This should never happen!\n");
7649     }
7650 #endif
7651 #endif
7652
7653     Moving2Blocked(x, y, &newx, &newy);
7654
7655     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7656       return;
7657
7658     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7659         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7660     {
7661       WasJustMoving[x][y] = 0;
7662       CheckCollision[x][y] = 0;
7663
7664       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7665
7666       if (Feld[x][y] != element)        /* element has changed */
7667         return;
7668     }
7669
7670     if (!MovDelay[x][y])        /* start new movement phase */
7671     {
7672       /* all objects that can change their move direction after each step
7673          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7674
7675       if (element != EL_YAMYAM &&
7676           element != EL_DARK_YAMYAM &&
7677           element != EL_PACMAN &&
7678           !(move_pattern & MV_ANY_DIRECTION) &&
7679           move_pattern != MV_TURNING_LEFT &&
7680           move_pattern != MV_TURNING_RIGHT &&
7681           move_pattern != MV_TURNING_LEFT_RIGHT &&
7682           move_pattern != MV_TURNING_RIGHT_LEFT &&
7683           move_pattern != MV_TURNING_RANDOM)
7684       {
7685         TurnRound(x, y);
7686
7687         if (MovDelay[x][y] && (element == EL_BUG ||
7688                                element == EL_SPACESHIP ||
7689                                element == EL_SP_SNIKSNAK ||
7690                                element == EL_SP_ELECTRON ||
7691                                element == EL_MOLE))
7692           DrawLevelField(x, y);
7693       }
7694     }
7695
7696     if (MovDelay[x][y])         /* wait some time before next movement */
7697     {
7698       MovDelay[x][y]--;
7699
7700       if (element == EL_ROBOT ||
7701           element == EL_YAMYAM ||
7702           element == EL_DARK_YAMYAM)
7703       {
7704         DrawLevelElementAnimationIfNeeded(x, y, element);
7705         PlayLevelSoundAction(x, y, ACTION_WAITING);
7706       }
7707       else if (element == EL_SP_ELECTRON)
7708         DrawLevelElementAnimationIfNeeded(x, y, element);
7709       else if (element == EL_DRAGON)
7710       {
7711         int i;
7712         int dir = MovDir[x][y];
7713         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7714         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7715         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7716                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7717                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7718                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7719         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7720
7721         GfxAction[x][y] = ACTION_ATTACKING;
7722
7723         if (IS_PLAYER(x, y))
7724           DrawPlayerField(x, y);
7725         else
7726           DrawLevelField(x, y);
7727
7728         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7729
7730         for (i = 1; i <= 3; i++)
7731         {
7732           int xx = x + i * dx;
7733           int yy = y + i * dy;
7734           int sx = SCREENX(xx);
7735           int sy = SCREENY(yy);
7736           int flame_graphic = graphic + (i - 1);
7737
7738           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7739             break;
7740
7741           if (MovDelay[x][y])
7742           {
7743             int flamed = MovingOrBlocked2Element(xx, yy);
7744
7745             /* !!! */
7746 #if 0
7747             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7748               Bang(xx, yy);
7749             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7750               RemoveMovingField(xx, yy);
7751             else
7752               RemoveField(xx, yy);
7753 #else
7754             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7755               Bang(xx, yy);
7756             else
7757               RemoveMovingField(xx, yy);
7758 #endif
7759
7760             ChangeDelay[xx][yy] = 0;
7761
7762             Feld[xx][yy] = EL_FLAMES;
7763
7764             if (IN_SCR_FIELD(sx, sy))
7765             {
7766               DrawLevelFieldCrumbledSand(xx, yy);
7767               DrawGraphic(sx, sy, flame_graphic, frame);
7768             }
7769           }
7770           else
7771           {
7772             if (Feld[xx][yy] == EL_FLAMES)
7773               Feld[xx][yy] = EL_EMPTY;
7774             DrawLevelField(xx, yy);
7775           }
7776         }
7777       }
7778
7779       if (MovDelay[x][y])       /* element still has to wait some time */
7780       {
7781         PlayLevelSoundAction(x, y, ACTION_WAITING);
7782
7783         return;
7784       }
7785     }
7786
7787     /* now make next step */
7788
7789     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7790
7791     if (DONT_COLLIDE_WITH(element) &&
7792         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7793         !PLAYER_ENEMY_PROTECTED(newx, newy))
7794     {
7795       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7796
7797       return;
7798     }
7799
7800     else if (CAN_MOVE_INTO_ACID(element) &&
7801              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7802              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7803              (MovDir[x][y] == MV_DOWN ||
7804               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7805     {
7806       SplashAcid(newx, newy);
7807       Store[x][y] = EL_ACID;
7808     }
7809     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7810     {
7811       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7812           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7813           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7814           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7815       {
7816         RemoveField(x, y);
7817         DrawLevelField(x, y);
7818
7819         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7820         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7821           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7822
7823         local_player->friends_still_needed--;
7824         if (!local_player->friends_still_needed &&
7825             !local_player->GameOver && AllPlayersGone)
7826           PlayerWins(local_player);
7827
7828         return;
7829       }
7830       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7831       {
7832         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7833           DrawLevelField(newx, newy);
7834         else
7835           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7836       }
7837       else if (!IS_FREE(newx, newy))
7838       {
7839         GfxAction[x][y] = ACTION_WAITING;
7840
7841         if (IS_PLAYER(x, y))
7842           DrawPlayerField(x, y);
7843         else
7844           DrawLevelField(x, y);
7845
7846         return;
7847       }
7848     }
7849     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7850     {
7851       if (IS_FOOD_PIG(Feld[newx][newy]))
7852       {
7853         if (IS_MOVING(newx, newy))
7854           RemoveMovingField(newx, newy);
7855         else
7856         {
7857           Feld[newx][newy] = EL_EMPTY;
7858           DrawLevelField(newx, newy);
7859         }
7860
7861         PlayLevelSound(x, y, SND_PIG_DIGGING);
7862       }
7863       else if (!IS_FREE(newx, newy))
7864       {
7865         if (IS_PLAYER(x, y))
7866           DrawPlayerField(x, y);
7867         else
7868           DrawLevelField(x, y);
7869
7870         return;
7871       }
7872     }
7873     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7874     {
7875       if (Store[x][y] != EL_EMPTY)
7876       {
7877         boolean can_clone = FALSE;
7878         int xx, yy;
7879
7880         /* check if element to clone is still there */
7881         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7882         {
7883           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7884           {
7885             can_clone = TRUE;
7886
7887             break;
7888           }
7889         }
7890
7891         /* cannot clone or target field not free anymore -- do not clone */
7892         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7893           Store[x][y] = EL_EMPTY;
7894       }
7895
7896       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7897       {
7898         if (IS_MV_DIAGONAL(MovDir[x][y]))
7899         {
7900           int diagonal_move_dir = MovDir[x][y];
7901           int stored = Store[x][y];
7902           int change_delay = 8;
7903           int graphic;
7904
7905           /* android is moving diagonally */
7906
7907           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7908
7909           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7910           GfxElement[x][y] = EL_EMC_ANDROID;
7911           GfxAction[x][y] = ACTION_SHRINKING;
7912           GfxDir[x][y] = diagonal_move_dir;
7913           ChangeDelay[x][y] = change_delay;
7914
7915           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7916                                    GfxDir[x][y]);
7917
7918           DrawLevelGraphicAnimation(x, y, graphic);
7919           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7920
7921           if (Feld[newx][newy] == EL_ACID)
7922           {
7923             SplashAcid(newx, newy);
7924
7925             return;
7926           }
7927
7928           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7929
7930           Store[newx][newy] = EL_EMC_ANDROID;
7931           GfxElement[newx][newy] = EL_EMC_ANDROID;
7932           GfxAction[newx][newy] = ACTION_GROWING;
7933           GfxDir[newx][newy] = diagonal_move_dir;
7934           ChangeDelay[newx][newy] = change_delay;
7935
7936           graphic = el_act_dir2img(GfxElement[newx][newy],
7937                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7938
7939           DrawLevelGraphicAnimation(newx, newy, graphic);
7940           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7941
7942           return;
7943         }
7944         else
7945         {
7946           Feld[newx][newy] = EL_EMPTY;
7947           DrawLevelField(newx, newy);
7948
7949           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7950         }
7951       }
7952       else if (!IS_FREE(newx, newy))
7953       {
7954 #if 0
7955         if (IS_PLAYER(x, y))
7956           DrawPlayerField(x, y);
7957         else
7958           DrawLevelField(x, y);
7959 #endif
7960
7961         return;
7962       }
7963     }
7964     else if (IS_CUSTOM_ELEMENT(element) &&
7965              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7966     {
7967       int new_element = Feld[newx][newy];
7968
7969       if (!IS_FREE(newx, newy))
7970       {
7971         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7972                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7973                       ACTION_BREAKING);
7974
7975         /* no element can dig solid indestructible elements */
7976         if (IS_INDESTRUCTIBLE(new_element) &&
7977             !IS_DIGGABLE(new_element) &&
7978             !IS_COLLECTIBLE(new_element))
7979           return;
7980
7981         if (AmoebaNr[newx][newy] &&
7982             (new_element == EL_AMOEBA_FULL ||
7983              new_element == EL_BD_AMOEBA ||
7984              new_element == EL_AMOEBA_GROWING))
7985         {
7986           AmoebaCnt[AmoebaNr[newx][newy]]--;
7987           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7988         }
7989
7990         if (IS_MOVING(newx, newy))
7991           RemoveMovingField(newx, newy);
7992         else
7993         {
7994           RemoveField(newx, newy);
7995           DrawLevelField(newx, newy);
7996         }
7997
7998         /* if digged element was about to explode, prevent the explosion */
7999         ExplodeField[newx][newy] = EX_TYPE_NONE;
8000
8001         PlayLevelSoundAction(x, y, action);
8002       }
8003
8004       Store[newx][newy] = EL_EMPTY;
8005 #if 1
8006       /* this makes it possible to leave the removed element again */
8007       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8008         Store[newx][newy] = new_element;
8009 #else
8010       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8011       {
8012         int move_leave_element = element_info[element].move_leave_element;
8013
8014         /* this makes it possible to leave the removed element again */
8015         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8016                              new_element : move_leave_element);
8017       }
8018 #endif
8019
8020       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8021       {
8022         RunnerVisit[x][y] = FrameCounter;
8023         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8024       }
8025     }
8026     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8027     {
8028       if (!IS_FREE(newx, newy))
8029       {
8030         if (IS_PLAYER(x, y))
8031           DrawPlayerField(x, y);
8032         else
8033           DrawLevelField(x, y);
8034
8035         return;
8036       }
8037       else
8038       {
8039         boolean wanna_flame = !RND(10);
8040         int dx = newx - x, dy = newy - y;
8041         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8042         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8043         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8044                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8045         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8046                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8047
8048         if ((wanna_flame ||
8049              IS_CLASSIC_ENEMY(element1) ||
8050              IS_CLASSIC_ENEMY(element2)) &&
8051             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8052             element1 != EL_FLAMES && element2 != EL_FLAMES)
8053         {
8054           ResetGfxAnimation(x, y);
8055           GfxAction[x][y] = ACTION_ATTACKING;
8056
8057           if (IS_PLAYER(x, y))
8058             DrawPlayerField(x, y);
8059           else
8060             DrawLevelField(x, y);
8061
8062           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8063
8064           MovDelay[x][y] = 50;
8065
8066           /* !!! */
8067 #if 0
8068           RemoveField(newx, newy);
8069 #endif
8070           Feld[newx][newy] = EL_FLAMES;
8071           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8072           {
8073 #if 0
8074             RemoveField(newx1, newy1);
8075 #endif
8076             Feld[newx1][newy1] = EL_FLAMES;
8077           }
8078           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8079           {
8080 #if 0
8081             RemoveField(newx2, newy2);
8082 #endif
8083             Feld[newx2][newy2] = EL_FLAMES;
8084           }
8085
8086           return;
8087         }
8088       }
8089     }
8090     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8091              Feld[newx][newy] == EL_DIAMOND)
8092     {
8093       if (IS_MOVING(newx, newy))
8094         RemoveMovingField(newx, newy);
8095       else
8096       {
8097         Feld[newx][newy] = EL_EMPTY;
8098         DrawLevelField(newx, newy);
8099       }
8100
8101       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8102     }
8103     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8104              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8105     {
8106       if (AmoebaNr[newx][newy])
8107       {
8108         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8109         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8110             Feld[newx][newy] == EL_BD_AMOEBA)
8111           AmoebaCnt[AmoebaNr[newx][newy]]--;
8112       }
8113
8114 #if 0
8115       /* !!! test !!! */
8116       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8117       {
8118         RemoveMovingField(newx, newy);
8119       }
8120 #else
8121       if (IS_MOVING(newx, newy))
8122       {
8123         RemoveMovingField(newx, newy);
8124       }
8125 #endif
8126       else
8127       {
8128         Feld[newx][newy] = EL_EMPTY;
8129         DrawLevelField(newx, newy);
8130       }
8131
8132       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8133     }
8134     else if ((element == EL_PACMAN || element == EL_MOLE)
8135              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8136     {
8137       if (AmoebaNr[newx][newy])
8138       {
8139         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8140         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8141             Feld[newx][newy] == EL_BD_AMOEBA)
8142           AmoebaCnt[AmoebaNr[newx][newy]]--;
8143       }
8144
8145       if (element == EL_MOLE)
8146       {
8147         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8148         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8149
8150         ResetGfxAnimation(x, y);
8151         GfxAction[x][y] = ACTION_DIGGING;
8152         DrawLevelField(x, y);
8153
8154         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8155
8156         return;                         /* wait for shrinking amoeba */
8157       }
8158       else      /* element == EL_PACMAN */
8159       {
8160         Feld[newx][newy] = EL_EMPTY;
8161         DrawLevelField(newx, newy);
8162         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8163       }
8164     }
8165     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8166              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8167               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8168     {
8169       /* wait for shrinking amoeba to completely disappear */
8170       return;
8171     }
8172     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8173     {
8174       /* object was running against a wall */
8175
8176       TurnRound(x, y);
8177
8178 #if 0
8179       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8180       if (move_pattern & MV_ANY_DIRECTION &&
8181           move_pattern == MovDir[x][y])
8182       {
8183         int blocking_element =
8184           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8185
8186         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8187                                  MovDir[x][y]);
8188
8189         element = Feld[x][y];   /* element might have changed */
8190       }
8191 #endif
8192
8193       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8194         DrawLevelElementAnimation(x, y, element);
8195
8196       if (DONT_TOUCH(element))
8197         TestIfBadThingTouchesPlayer(x, y);
8198
8199       return;
8200     }
8201
8202     InitMovingField(x, y, MovDir[x][y]);
8203
8204     PlayLevelSoundAction(x, y, ACTION_MOVING);
8205   }
8206
8207   if (MovDir[x][y])
8208     ContinueMoving(x, y);
8209 }
8210
8211 void ContinueMoving(int x, int y)
8212 {
8213   int element = Feld[x][y];
8214   struct ElementInfo *ei = &element_info[element];
8215   int direction = MovDir[x][y];
8216   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8217   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8218   int newx = x + dx, newy = y + dy;
8219   int stored = Store[x][y];
8220   int stored_new = Store[newx][newy];
8221   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8222   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8223   boolean last_line = (newy == lev_fieldy - 1);
8224
8225   MovPos[x][y] += getElementMoveStepsize(x, y);
8226
8227   if (pushed_by_player) /* special case: moving object pushed by player */
8228     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8229
8230   if (ABS(MovPos[x][y]) < TILEX)
8231   {
8232 #if 0
8233     int ee = Feld[x][y];
8234     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8235     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8236
8237     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8238            x, y, ABS(MovPos[x][y]),
8239            ee, gg, ff,
8240            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8241 #endif
8242
8243     DrawLevelField(x, y);
8244
8245     return;     /* element is still moving */
8246   }
8247
8248   /* element reached destination field */
8249
8250   Feld[x][y] = EL_EMPTY;
8251   Feld[newx][newy] = element;
8252   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8253
8254   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8255   {
8256     element = Feld[newx][newy] = EL_ACID;
8257   }
8258   else if (element == EL_MOLE)
8259   {
8260     Feld[x][y] = EL_SAND;
8261
8262     DrawLevelFieldCrumbledSandNeighbours(x, y);
8263   }
8264   else if (element == EL_QUICKSAND_FILLING)
8265   {
8266     element = Feld[newx][newy] = get_next_element(element);
8267     Store[newx][newy] = Store[x][y];
8268   }
8269   else if (element == EL_QUICKSAND_EMPTYING)
8270   {
8271     Feld[x][y] = get_next_element(element);
8272     element = Feld[newx][newy] = Store[x][y];
8273   }
8274   else if (element == EL_QUICKSAND_FAST_FILLING)
8275   {
8276     element = Feld[newx][newy] = get_next_element(element);
8277     Store[newx][newy] = Store[x][y];
8278   }
8279   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8280   {
8281     Feld[x][y] = get_next_element(element);
8282     element = Feld[newx][newy] = Store[x][y];
8283   }
8284   else if (element == EL_MAGIC_WALL_FILLING)
8285   {
8286     element = Feld[newx][newy] = get_next_element(element);
8287     if (!game.magic_wall_active)
8288       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8289     Store[newx][newy] = Store[x][y];
8290   }
8291   else if (element == EL_MAGIC_WALL_EMPTYING)
8292   {
8293     Feld[x][y] = get_next_element(element);
8294     if (!game.magic_wall_active)
8295       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8296     element = Feld[newx][newy] = Store[x][y];
8297
8298 #if USE_NEW_CUSTOM_VALUE
8299     InitField(newx, newy, FALSE);
8300 #endif
8301   }
8302   else if (element == EL_BD_MAGIC_WALL_FILLING)
8303   {
8304     element = Feld[newx][newy] = get_next_element(element);
8305     if (!game.magic_wall_active)
8306       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8307     Store[newx][newy] = Store[x][y];
8308   }
8309   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8310   {
8311     Feld[x][y] = get_next_element(element);
8312     if (!game.magic_wall_active)
8313       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8314     element = Feld[newx][newy] = Store[x][y];
8315
8316 #if USE_NEW_CUSTOM_VALUE
8317     InitField(newx, newy, FALSE);
8318 #endif
8319   }
8320   else if (element == EL_DC_MAGIC_WALL_FILLING)
8321   {
8322     element = Feld[newx][newy] = get_next_element(element);
8323     if (!game.magic_wall_active)
8324       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8325     Store[newx][newy] = Store[x][y];
8326   }
8327   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8328   {
8329     Feld[x][y] = get_next_element(element);
8330     if (!game.magic_wall_active)
8331       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8332     element = Feld[newx][newy] = Store[x][y];
8333
8334 #if USE_NEW_CUSTOM_VALUE
8335     InitField(newx, newy, FALSE);
8336 #endif
8337   }
8338   else if (element == EL_AMOEBA_DROPPING)
8339   {
8340     Feld[x][y] = get_next_element(element);
8341     element = Feld[newx][newy] = Store[x][y];
8342   }
8343   else if (element == EL_SOKOBAN_OBJECT)
8344   {
8345     if (Back[x][y])
8346       Feld[x][y] = Back[x][y];
8347
8348     if (Back[newx][newy])
8349       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8350
8351     Back[x][y] = Back[newx][newy] = 0;
8352   }
8353
8354   Store[x][y] = EL_EMPTY;
8355   MovPos[x][y] = 0;
8356   MovDir[x][y] = 0;
8357   MovDelay[x][y] = 0;
8358
8359   MovDelay[newx][newy] = 0;
8360
8361   if (CAN_CHANGE_OR_HAS_ACTION(element))
8362   {
8363     /* copy element change control values to new field */
8364     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8365     ChangePage[newx][newy]  = ChangePage[x][y];
8366     ChangeCount[newx][newy] = ChangeCount[x][y];
8367     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8368   }
8369
8370 #if USE_NEW_CUSTOM_VALUE
8371     CustomValue[newx][newy] = CustomValue[x][y];
8372 #endif
8373
8374   ChangeDelay[x][y] = 0;
8375   ChangePage[x][y] = -1;
8376   ChangeCount[x][y] = 0;
8377   ChangeEvent[x][y] = -1;
8378
8379 #if USE_NEW_CUSTOM_VALUE
8380   CustomValue[x][y] = 0;
8381 #endif
8382
8383   /* copy animation control values to new field */
8384   GfxFrame[newx][newy]  = GfxFrame[x][y];
8385   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8386   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8387   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8388
8389   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8390
8391   /* some elements can leave other elements behind after moving */
8392 #if 1
8393   if (ei->move_leave_element != EL_EMPTY &&
8394       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8395       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8396 #else
8397   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8398       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8399       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8400 #endif
8401   {
8402     int move_leave_element = ei->move_leave_element;
8403
8404 #if 1
8405 #if 1
8406     /* this makes it possible to leave the removed element again */
8407     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8408       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8409 #else
8410     /* this makes it possible to leave the removed element again */
8411     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8412       move_leave_element = stored;
8413 #endif
8414 #else
8415     /* this makes it possible to leave the removed element again */
8416     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8417         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8418       move_leave_element = stored;
8419 #endif
8420
8421     Feld[x][y] = move_leave_element;
8422
8423     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8424       MovDir[x][y] = direction;
8425
8426     InitField(x, y, FALSE);
8427
8428     if (GFX_CRUMBLED(Feld[x][y]))
8429       DrawLevelFieldCrumbledSandNeighbours(x, y);
8430
8431     if (ELEM_IS_PLAYER(move_leave_element))
8432       RelocatePlayer(x, y, move_leave_element);
8433   }
8434
8435   /* do this after checking for left-behind element */
8436   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8437
8438   if (!CAN_MOVE(element) ||
8439       (CAN_FALL(element) && direction == MV_DOWN &&
8440        (element == EL_SPRING ||
8441         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8442         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8443     GfxDir[x][y] = MovDir[newx][newy] = 0;
8444
8445   DrawLevelField(x, y);
8446   DrawLevelField(newx, newy);
8447
8448   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8449
8450   /* prevent pushed element from moving on in pushed direction */
8451   if (pushed_by_player && CAN_MOVE(element) &&
8452       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8453       !(element_info[element].move_pattern & direction))
8454     TurnRound(newx, newy);
8455
8456   /* prevent elements on conveyor belt from moving on in last direction */
8457   if (pushed_by_conveyor && CAN_FALL(element) &&
8458       direction & MV_HORIZONTAL)
8459     MovDir[newx][newy] = 0;
8460
8461   if (!pushed_by_player)
8462   {
8463     int nextx = newx + dx, nexty = newy + dy;
8464     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8465
8466     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8467
8468     if (CAN_FALL(element) && direction == MV_DOWN)
8469       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8470
8471     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8472       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8473
8474 #if USE_FIX_IMPACT_COLLISION
8475     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8476       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8477 #endif
8478   }
8479
8480   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8481   {
8482     TestIfBadThingTouchesPlayer(newx, newy);
8483     TestIfBadThingTouchesFriend(newx, newy);
8484
8485     if (!IS_CUSTOM_ELEMENT(element))
8486       TestIfBadThingTouchesOtherBadThing(newx, newy);
8487   }
8488   else if (element == EL_PENGUIN)
8489     TestIfFriendTouchesBadThing(newx, newy);
8490
8491   /* give the player one last chance (one more frame) to move away */
8492   if (CAN_FALL(element) && direction == MV_DOWN &&
8493       (last_line || (!IS_FREE(x, newy + 1) &&
8494                      (!IS_PLAYER(x, newy + 1) ||
8495                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8496     Impact(x, newy);
8497
8498   if (pushed_by_player && !game.use_change_when_pushing_bug)
8499   {
8500     int push_side = MV_DIR_OPPOSITE(direction);
8501     struct PlayerInfo *player = PLAYERINFO(x, y);
8502
8503     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8504                                player->index_bit, push_side);
8505     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8506                                         player->index_bit, push_side);
8507   }
8508
8509   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8510     MovDelay[newx][newy] = 1;
8511
8512   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8513
8514   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8515
8516 #if 0
8517   if (ChangePage[newx][newy] != -1)             /* delayed change */
8518   {
8519     int page = ChangePage[newx][newy];
8520     struct ElementChangeInfo *change = &ei->change_page[page];
8521
8522     ChangePage[newx][newy] = -1;
8523
8524     if (change->can_change)
8525     {
8526       if (ChangeElement(newx, newy, element, page))
8527       {
8528         if (change->post_change_function)
8529           change->post_change_function(newx, newy);
8530       }
8531     }
8532
8533     if (change->has_action)
8534       ExecuteCustomElementAction(newx, newy, element, page);
8535   }
8536 #endif
8537
8538   TestIfElementHitsCustomElement(newx, newy, direction);
8539   TestIfPlayerTouchesCustomElement(newx, newy);
8540   TestIfElementTouchesCustomElement(newx, newy);
8541
8542   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8543       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8544     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8545                              MV_DIR_OPPOSITE(direction));
8546 }
8547
8548 int AmoebeNachbarNr(int ax, int ay)
8549 {
8550   int i;
8551   int element = Feld[ax][ay];
8552   int group_nr = 0;
8553   static int xy[4][2] =
8554   {
8555     { 0, -1 },
8556     { -1, 0 },
8557     { +1, 0 },
8558     { 0, +1 }
8559   };
8560
8561   for (i = 0; i < NUM_DIRECTIONS; i++)
8562   {
8563     int x = ax + xy[i][0];
8564     int y = ay + xy[i][1];
8565
8566     if (!IN_LEV_FIELD(x, y))
8567       continue;
8568
8569     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8570       group_nr = AmoebaNr[x][y];
8571   }
8572
8573   return group_nr;
8574 }
8575
8576 void AmoebenVereinigen(int ax, int ay)
8577 {
8578   int i, x, y, xx, yy;
8579   int new_group_nr = AmoebaNr[ax][ay];
8580   static int xy[4][2] =
8581   {
8582     { 0, -1 },
8583     { -1, 0 },
8584     { +1, 0 },
8585     { 0, +1 }
8586   };
8587
8588   if (new_group_nr == 0)
8589     return;
8590
8591   for (i = 0; i < NUM_DIRECTIONS; i++)
8592   {
8593     x = ax + xy[i][0];
8594     y = ay + xy[i][1];
8595
8596     if (!IN_LEV_FIELD(x, y))
8597       continue;
8598
8599     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8600          Feld[x][y] == EL_BD_AMOEBA ||
8601          Feld[x][y] == EL_AMOEBA_DEAD) &&
8602         AmoebaNr[x][y] != new_group_nr)
8603     {
8604       int old_group_nr = AmoebaNr[x][y];
8605
8606       if (old_group_nr == 0)
8607         return;
8608
8609       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8610       AmoebaCnt[old_group_nr] = 0;
8611       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8612       AmoebaCnt2[old_group_nr] = 0;
8613
8614       SCAN_PLAYFIELD(xx, yy)
8615       {
8616         if (AmoebaNr[xx][yy] == old_group_nr)
8617           AmoebaNr[xx][yy] = new_group_nr;
8618       }
8619     }
8620   }
8621 }
8622
8623 void AmoebeUmwandeln(int ax, int ay)
8624 {
8625   int i, x, y;
8626
8627   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8628   {
8629     int group_nr = AmoebaNr[ax][ay];
8630
8631 #ifdef DEBUG
8632     if (group_nr == 0)
8633     {
8634       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8635       printf("AmoebeUmwandeln(): This should never happen!\n");
8636       return;
8637     }
8638 #endif
8639
8640     SCAN_PLAYFIELD(x, y)
8641     {
8642       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8643       {
8644         AmoebaNr[x][y] = 0;
8645         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8646       }
8647     }
8648
8649     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8650                             SND_AMOEBA_TURNING_TO_GEM :
8651                             SND_AMOEBA_TURNING_TO_ROCK));
8652     Bang(ax, ay);
8653   }
8654   else
8655   {
8656     static int xy[4][2] =
8657     {
8658       { 0, -1 },
8659       { -1, 0 },
8660       { +1, 0 },
8661       { 0, +1 }
8662     };
8663
8664     for (i = 0; i < NUM_DIRECTIONS; i++)
8665     {
8666       x = ax + xy[i][0];
8667       y = ay + xy[i][1];
8668
8669       if (!IN_LEV_FIELD(x, y))
8670         continue;
8671
8672       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8673       {
8674         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8675                               SND_AMOEBA_TURNING_TO_GEM :
8676                               SND_AMOEBA_TURNING_TO_ROCK));
8677         Bang(x, y);
8678       }
8679     }
8680   }
8681 }
8682
8683 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8684 {
8685   int x, y;
8686   int group_nr = AmoebaNr[ax][ay];
8687   boolean done = FALSE;
8688
8689 #ifdef DEBUG
8690   if (group_nr == 0)
8691   {
8692     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8693     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8694     return;
8695   }
8696 #endif
8697
8698   SCAN_PLAYFIELD(x, y)
8699   {
8700     if (AmoebaNr[x][y] == group_nr &&
8701         (Feld[x][y] == EL_AMOEBA_DEAD ||
8702          Feld[x][y] == EL_BD_AMOEBA ||
8703          Feld[x][y] == EL_AMOEBA_GROWING))
8704     {
8705       AmoebaNr[x][y] = 0;
8706       Feld[x][y] = new_element;
8707       InitField(x, y, FALSE);
8708       DrawLevelField(x, y);
8709       done = TRUE;
8710     }
8711   }
8712
8713   if (done)
8714     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8715                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8716                             SND_BD_AMOEBA_TURNING_TO_GEM));
8717 }
8718
8719 void AmoebeWaechst(int x, int y)
8720 {
8721   static unsigned long sound_delay = 0;
8722   static unsigned long sound_delay_value = 0;
8723
8724   if (!MovDelay[x][y])          /* start new growing cycle */
8725   {
8726     MovDelay[x][y] = 7;
8727
8728     if (DelayReached(&sound_delay, sound_delay_value))
8729     {
8730       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8731       sound_delay_value = 30;
8732     }
8733   }
8734
8735   if (MovDelay[x][y])           /* wait some time before growing bigger */
8736   {
8737     MovDelay[x][y]--;
8738     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8739     {
8740       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8741                                            6 - MovDelay[x][y]);
8742
8743       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8744     }
8745
8746     if (!MovDelay[x][y])
8747     {
8748       Feld[x][y] = Store[x][y];
8749       Store[x][y] = 0;
8750       DrawLevelField(x, y);
8751     }
8752   }
8753 }
8754
8755 void AmoebaDisappearing(int x, int y)
8756 {
8757   static unsigned long sound_delay = 0;
8758   static unsigned long sound_delay_value = 0;
8759
8760   if (!MovDelay[x][y])          /* start new shrinking cycle */
8761   {
8762     MovDelay[x][y] = 7;
8763
8764     if (DelayReached(&sound_delay, sound_delay_value))
8765       sound_delay_value = 30;
8766   }
8767
8768   if (MovDelay[x][y])           /* wait some time before shrinking */
8769   {
8770     MovDelay[x][y]--;
8771     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8772     {
8773       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8774                                            6 - MovDelay[x][y]);
8775
8776       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8777     }
8778
8779     if (!MovDelay[x][y])
8780     {
8781       Feld[x][y] = EL_EMPTY;
8782       DrawLevelField(x, y);
8783
8784       /* don't let mole enter this field in this cycle;
8785          (give priority to objects falling to this field from above) */
8786       Stop[x][y] = TRUE;
8787     }
8788   }
8789 }
8790
8791 void AmoebeAbleger(int ax, int ay)
8792 {
8793   int i;
8794   int element = Feld[ax][ay];
8795   int graphic = el2img(element);
8796   int newax = ax, neway = ay;
8797   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8798   static int xy[4][2] =
8799   {
8800     { 0, -1 },
8801     { -1, 0 },
8802     { +1, 0 },
8803     { 0, +1 }
8804   };
8805
8806   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8807   {
8808     Feld[ax][ay] = EL_AMOEBA_DEAD;
8809     DrawLevelField(ax, ay);
8810     return;
8811   }
8812
8813   if (IS_ANIMATED(graphic))
8814     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8815
8816   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8817     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8818
8819   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8820   {
8821     MovDelay[ax][ay]--;
8822     if (MovDelay[ax][ay])
8823       return;
8824   }
8825
8826   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8827   {
8828     int start = RND(4);
8829     int x = ax + xy[start][0];
8830     int y = ay + xy[start][1];
8831
8832     if (!IN_LEV_FIELD(x, y))
8833       return;
8834
8835     if (IS_FREE(x, y) ||
8836         CAN_GROW_INTO(Feld[x][y]) ||
8837         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8838         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8839     {
8840       newax = x;
8841       neway = y;
8842     }
8843
8844     if (newax == ax && neway == ay)
8845       return;
8846   }
8847   else                          /* normal or "filled" (BD style) amoeba */
8848   {
8849     int start = RND(4);
8850     boolean waiting_for_player = FALSE;
8851
8852     for (i = 0; i < NUM_DIRECTIONS; i++)
8853     {
8854       int j = (start + i) % 4;
8855       int x = ax + xy[j][0];
8856       int y = ay + xy[j][1];
8857
8858       if (!IN_LEV_FIELD(x, y))
8859         continue;
8860
8861       if (IS_FREE(x, y) ||
8862           CAN_GROW_INTO(Feld[x][y]) ||
8863           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8864           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8865       {
8866         newax = x;
8867         neway = y;
8868         break;
8869       }
8870       else if (IS_PLAYER(x, y))
8871         waiting_for_player = TRUE;
8872     }
8873
8874     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8875     {
8876       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8877       {
8878         Feld[ax][ay] = EL_AMOEBA_DEAD;
8879         DrawLevelField(ax, ay);
8880         AmoebaCnt[AmoebaNr[ax][ay]]--;
8881
8882         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8883         {
8884           if (element == EL_AMOEBA_FULL)
8885             AmoebeUmwandeln(ax, ay);
8886           else if (element == EL_BD_AMOEBA)
8887             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8888         }
8889       }
8890       return;
8891     }
8892     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8893     {
8894       /* amoeba gets larger by growing in some direction */
8895
8896       int new_group_nr = AmoebaNr[ax][ay];
8897
8898 #ifdef DEBUG
8899   if (new_group_nr == 0)
8900   {
8901     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8902     printf("AmoebeAbleger(): This should never happen!\n");
8903     return;
8904   }
8905 #endif
8906
8907       AmoebaNr[newax][neway] = new_group_nr;
8908       AmoebaCnt[new_group_nr]++;
8909       AmoebaCnt2[new_group_nr]++;
8910
8911       /* if amoeba touches other amoeba(s) after growing, unify them */
8912       AmoebenVereinigen(newax, neway);
8913
8914       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8915       {
8916         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8917         return;
8918       }
8919     }
8920   }
8921
8922   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8923       (neway == lev_fieldy - 1 && newax != ax))
8924   {
8925     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8926     Store[newax][neway] = element;
8927   }
8928   else if (neway == ay || element == EL_EMC_DRIPPER)
8929   {
8930     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8931
8932     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8933   }
8934   else
8935   {
8936     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8937     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8938     Store[ax][ay] = EL_AMOEBA_DROP;
8939     ContinueMoving(ax, ay);
8940     return;
8941   }
8942
8943   DrawLevelField(newax, neway);
8944 }
8945
8946 void Life(int ax, int ay)
8947 {
8948   int x1, y1, x2, y2;
8949   int life_time = 40;
8950   int element = Feld[ax][ay];
8951   int graphic = el2img(element);
8952   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8953                          level.biomaze);
8954   boolean changed = FALSE;
8955
8956   if (IS_ANIMATED(graphic))
8957     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8958
8959   if (Stop[ax][ay])
8960     return;
8961
8962   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8963     MovDelay[ax][ay] = life_time;
8964
8965   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8966   {
8967     MovDelay[ax][ay]--;
8968     if (MovDelay[ax][ay])
8969       return;
8970   }
8971
8972   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8973   {
8974     int xx = ax+x1, yy = ay+y1;
8975     int nachbarn = 0;
8976
8977     if (!IN_LEV_FIELD(xx, yy))
8978       continue;
8979
8980     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8981     {
8982       int x = xx+x2, y = yy+y2;
8983
8984       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8985         continue;
8986
8987       if (((Feld[x][y] == element ||
8988             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8989            !Stop[x][y]) ||
8990           (IS_FREE(x, y) && Stop[x][y]))
8991         nachbarn++;
8992     }
8993
8994     if (xx == ax && yy == ay)           /* field in the middle */
8995     {
8996       if (nachbarn < life_parameter[0] ||
8997           nachbarn > life_parameter[1])
8998       {
8999         Feld[xx][yy] = EL_EMPTY;
9000         if (!Stop[xx][yy])
9001           DrawLevelField(xx, yy);
9002         Stop[xx][yy] = TRUE;
9003         changed = TRUE;
9004       }
9005     }
9006     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9007     {                                   /* free border field */
9008       if (nachbarn >= life_parameter[2] &&
9009           nachbarn <= life_parameter[3])
9010       {
9011         Feld[xx][yy] = element;
9012         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9013         if (!Stop[xx][yy])
9014           DrawLevelField(xx, yy);
9015         Stop[xx][yy] = TRUE;
9016         changed = TRUE;
9017       }
9018     }
9019   }
9020
9021   if (changed)
9022     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9023                    SND_GAME_OF_LIFE_GROWING);
9024 }
9025
9026 static void InitRobotWheel(int x, int y)
9027 {
9028   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9029 }
9030
9031 static void RunRobotWheel(int x, int y)
9032 {
9033   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9034 }
9035
9036 static void StopRobotWheel(int x, int y)
9037 {
9038   if (ZX == x && ZY == y)
9039   {
9040     ZX = ZY = -1;
9041
9042     game.robot_wheel_active = FALSE;
9043   }
9044 }
9045
9046 static void InitTimegateWheel(int x, int y)
9047 {
9048   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9049 }
9050
9051 static void RunTimegateWheel(int x, int y)
9052 {
9053   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9054 }
9055
9056 static void InitMagicBallDelay(int x, int y)
9057 {
9058 #if 1
9059   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9060 #else
9061   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9062 #endif
9063 }
9064
9065 static void ActivateMagicBall(int bx, int by)
9066 {
9067   int x, y;
9068
9069   if (level.ball_random)
9070   {
9071     int pos_border = RND(8);    /* select one of the eight border elements */
9072     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9073     int xx = pos_content % 3;
9074     int yy = pos_content / 3;
9075
9076     x = bx - 1 + xx;
9077     y = by - 1 + yy;
9078
9079     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9080       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9081   }
9082   else
9083   {
9084     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9085     {
9086       int xx = x - bx + 1;
9087       int yy = y - by + 1;
9088
9089       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9090         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9091     }
9092   }
9093
9094   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9095 }
9096
9097 void CheckExit(int x, int y)
9098 {
9099   if (local_player->gems_still_needed > 0 ||
9100       local_player->sokobanfields_still_needed > 0 ||
9101       local_player->lights_still_needed > 0)
9102   {
9103     int element = Feld[x][y];
9104     int graphic = el2img(element);
9105
9106     if (IS_ANIMATED(graphic))
9107       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9108
9109     return;
9110   }
9111
9112   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9113     return;
9114
9115   Feld[x][y] = EL_EXIT_OPENING;
9116
9117   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9118 }
9119
9120 void CheckExitEM(int x, int y)
9121 {
9122   if (local_player->gems_still_needed > 0 ||
9123       local_player->sokobanfields_still_needed > 0 ||
9124       local_player->lights_still_needed > 0)
9125   {
9126     int element = Feld[x][y];
9127     int graphic = el2img(element);
9128
9129     if (IS_ANIMATED(graphic))
9130       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9131
9132     return;
9133   }
9134
9135   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9136     return;
9137
9138   Feld[x][y] = EL_EM_EXIT_OPENING;
9139
9140   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9141 }
9142
9143 void CheckExitSteel(int x, int y)
9144 {
9145   if (local_player->gems_still_needed > 0 ||
9146       local_player->sokobanfields_still_needed > 0 ||
9147       local_player->lights_still_needed > 0)
9148   {
9149     int element = Feld[x][y];
9150     int graphic = el2img(element);
9151
9152     if (IS_ANIMATED(graphic))
9153       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9154
9155     return;
9156   }
9157
9158   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9159     return;
9160
9161   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9162
9163   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9164 }
9165
9166 void CheckExitSteelEM(int x, int y)
9167 {
9168   if (local_player->gems_still_needed > 0 ||
9169       local_player->sokobanfields_still_needed > 0 ||
9170       local_player->lights_still_needed > 0)
9171   {
9172     int element = Feld[x][y];
9173     int graphic = el2img(element);
9174
9175     if (IS_ANIMATED(graphic))
9176       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9177
9178     return;
9179   }
9180
9181   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9182     return;
9183
9184   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9185
9186   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9187 }
9188
9189 void CheckExitSP(int x, int y)
9190 {
9191   if (local_player->gems_still_needed > 0)
9192   {
9193     int element = Feld[x][y];
9194     int graphic = el2img(element);
9195
9196     if (IS_ANIMATED(graphic))
9197       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9198
9199     return;
9200   }
9201
9202   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9203     return;
9204
9205   Feld[x][y] = EL_SP_EXIT_OPENING;
9206
9207   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9208 }
9209
9210 static void CloseAllOpenTimegates()
9211 {
9212   int x, y;
9213
9214   SCAN_PLAYFIELD(x, y)
9215   {
9216     int element = Feld[x][y];
9217
9218     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9219     {
9220       Feld[x][y] = EL_TIMEGATE_CLOSING;
9221
9222       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9223     }
9224   }
9225 }
9226
9227 void DrawTwinkleOnField(int x, int y)
9228 {
9229   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9230     return;
9231
9232   if (Feld[x][y] == EL_BD_DIAMOND)
9233     return;
9234
9235   if (MovDelay[x][y] == 0)      /* next animation frame */
9236     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9237
9238   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9239   {
9240     MovDelay[x][y]--;
9241
9242     if (setup.direct_draw && MovDelay[x][y])
9243       SetDrawtoField(DRAW_BUFFERED);
9244
9245     DrawLevelElementAnimation(x, y, Feld[x][y]);
9246
9247     if (MovDelay[x][y] != 0)
9248     {
9249       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9250                                            10 - MovDelay[x][y]);
9251
9252       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9253
9254       if (setup.direct_draw)
9255       {
9256         int dest_x, dest_y;
9257
9258         dest_x = FX + SCREENX(x) * TILEX;
9259         dest_y = FY + SCREENY(y) * TILEY;
9260
9261         BlitBitmap(drawto_field, window,
9262                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9263         SetDrawtoField(DRAW_DIRECT);
9264       }
9265     }
9266   }
9267 }
9268
9269 void MauerWaechst(int x, int y)
9270 {
9271   int delay = 6;
9272
9273   if (!MovDelay[x][y])          /* next animation frame */
9274     MovDelay[x][y] = 3 * delay;
9275
9276   if (MovDelay[x][y])           /* wait some time before next frame */
9277   {
9278     MovDelay[x][y]--;
9279
9280     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9281     {
9282       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9283       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9284
9285       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9286     }
9287
9288     if (!MovDelay[x][y])
9289     {
9290       if (MovDir[x][y] == MV_LEFT)
9291       {
9292         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9293           DrawLevelField(x - 1, y);
9294       }
9295       else if (MovDir[x][y] == MV_RIGHT)
9296       {
9297         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9298           DrawLevelField(x + 1, y);
9299       }
9300       else if (MovDir[x][y] == MV_UP)
9301       {
9302         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9303           DrawLevelField(x, y - 1);
9304       }
9305       else
9306       {
9307         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9308           DrawLevelField(x, y + 1);
9309       }
9310
9311       Feld[x][y] = Store[x][y];
9312       Store[x][y] = 0;
9313       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9314       DrawLevelField(x, y);
9315     }
9316   }
9317 }
9318
9319 void MauerAbleger(int ax, int ay)
9320 {
9321   int element = Feld[ax][ay];
9322   int graphic = el2img(element);
9323   boolean oben_frei = FALSE, unten_frei = FALSE;
9324   boolean links_frei = FALSE, rechts_frei = FALSE;
9325   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9326   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9327   boolean new_wall = FALSE;
9328
9329   if (IS_ANIMATED(graphic))
9330     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9331
9332   if (!MovDelay[ax][ay])        /* start building new wall */
9333     MovDelay[ax][ay] = 6;
9334
9335   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9336   {
9337     MovDelay[ax][ay]--;
9338     if (MovDelay[ax][ay])
9339       return;
9340   }
9341
9342   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9343     oben_frei = TRUE;
9344   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9345     unten_frei = TRUE;
9346   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9347     links_frei = TRUE;
9348   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9349     rechts_frei = TRUE;
9350
9351   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9352       element == EL_EXPANDABLE_WALL_ANY)
9353   {
9354     if (oben_frei)
9355     {
9356       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9357       Store[ax][ay-1] = element;
9358       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9359       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9360         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9361                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9362       new_wall = TRUE;
9363     }
9364     if (unten_frei)
9365     {
9366       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9367       Store[ax][ay+1] = element;
9368       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9369       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9370         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9371                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9372       new_wall = TRUE;
9373     }
9374   }
9375
9376   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9377       element == EL_EXPANDABLE_WALL_ANY ||
9378       element == EL_EXPANDABLE_WALL ||
9379       element == EL_BD_EXPANDABLE_WALL)
9380   {
9381     if (links_frei)
9382     {
9383       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9384       Store[ax-1][ay] = element;
9385       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9386       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9387         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9388                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9389       new_wall = TRUE;
9390     }
9391
9392     if (rechts_frei)
9393     {
9394       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9395       Store[ax+1][ay] = element;
9396       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9397       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9398         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9399                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9400       new_wall = TRUE;
9401     }
9402   }
9403
9404   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9405     DrawLevelField(ax, ay);
9406
9407   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9408     oben_massiv = TRUE;
9409   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9410     unten_massiv = TRUE;
9411   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9412     links_massiv = TRUE;
9413   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9414     rechts_massiv = TRUE;
9415
9416   if (((oben_massiv && unten_massiv) ||
9417        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9418        element == EL_EXPANDABLE_WALL) &&
9419       ((links_massiv && rechts_massiv) ||
9420        element == EL_EXPANDABLE_WALL_VERTICAL))
9421     Feld[ax][ay] = EL_WALL;
9422
9423   if (new_wall)
9424     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9425 }
9426
9427 void MauerAblegerStahl(int ax, int ay)
9428 {
9429   int element = Feld[ax][ay];
9430   int graphic = el2img(element);
9431   boolean oben_frei = FALSE, unten_frei = FALSE;
9432   boolean links_frei = FALSE, rechts_frei = FALSE;
9433   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9434   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9435   boolean new_wall = FALSE;
9436
9437   if (IS_ANIMATED(graphic))
9438     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9439
9440   if (!MovDelay[ax][ay])        /* start building new wall */
9441     MovDelay[ax][ay] = 6;
9442
9443   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9444   {
9445     MovDelay[ax][ay]--;
9446     if (MovDelay[ax][ay])
9447       return;
9448   }
9449
9450   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9451     oben_frei = TRUE;
9452   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9453     unten_frei = TRUE;
9454   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9455     links_frei = TRUE;
9456   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9457     rechts_frei = TRUE;
9458
9459   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9460       element == EL_EXPANDABLE_STEELWALL_ANY)
9461   {
9462     if (oben_frei)
9463     {
9464       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9465       Store[ax][ay-1] = element;
9466       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9467       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9468         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9469                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9470       new_wall = TRUE;
9471     }
9472     if (unten_frei)
9473     {
9474       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9475       Store[ax][ay+1] = element;
9476       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9477       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9478         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9479                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9480       new_wall = TRUE;
9481     }
9482   }
9483
9484   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9485       element == EL_EXPANDABLE_STEELWALL_ANY)
9486   {
9487     if (links_frei)
9488     {
9489       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9490       Store[ax-1][ay] = element;
9491       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9492       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9493         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9494                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9495       new_wall = TRUE;
9496     }
9497
9498     if (rechts_frei)
9499     {
9500       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9501       Store[ax+1][ay] = element;
9502       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9503       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9504         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9505                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9506       new_wall = TRUE;
9507     }
9508   }
9509
9510   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9511     oben_massiv = TRUE;
9512   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9513     unten_massiv = TRUE;
9514   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9515     links_massiv = TRUE;
9516   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9517     rechts_massiv = TRUE;
9518
9519   if (((oben_massiv && unten_massiv) ||
9520        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9521       ((links_massiv && rechts_massiv) ||
9522        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9523     Feld[ax][ay] = EL_WALL;
9524
9525   if (new_wall)
9526     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9527 }
9528
9529 void CheckForDragon(int x, int y)
9530 {
9531   int i, j;
9532   boolean dragon_found = FALSE;
9533   static int xy[4][2] =
9534   {
9535     { 0, -1 },
9536     { -1, 0 },
9537     { +1, 0 },
9538     { 0, +1 }
9539   };
9540
9541   for (i = 0; i < NUM_DIRECTIONS; i++)
9542   {
9543     for (j = 0; j < 4; j++)
9544     {
9545       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9546
9547       if (IN_LEV_FIELD(xx, yy) &&
9548           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9549       {
9550         if (Feld[xx][yy] == EL_DRAGON)
9551           dragon_found = TRUE;
9552       }
9553       else
9554         break;
9555     }
9556   }
9557
9558   if (!dragon_found)
9559   {
9560     for (i = 0; i < NUM_DIRECTIONS; i++)
9561     {
9562       for (j = 0; j < 3; j++)
9563       {
9564         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9565   
9566         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9567         {
9568           Feld[xx][yy] = EL_EMPTY;
9569           DrawLevelField(xx, yy);
9570         }
9571         else
9572           break;
9573       }
9574     }
9575   }
9576 }
9577
9578 static void InitBuggyBase(int x, int y)
9579 {
9580   int element = Feld[x][y];
9581   int activating_delay = FRAMES_PER_SECOND / 4;
9582
9583   ChangeDelay[x][y] =
9584     (element == EL_SP_BUGGY_BASE ?
9585      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9586      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9587      activating_delay :
9588      element == EL_SP_BUGGY_BASE_ACTIVE ?
9589      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9590 }
9591
9592 static void WarnBuggyBase(int x, int y)
9593 {
9594   int i;
9595   static int xy[4][2] =
9596   {
9597     { 0, -1 },
9598     { -1, 0 },
9599     { +1, 0 },
9600     { 0, +1 }
9601   };
9602
9603   for (i = 0; i < NUM_DIRECTIONS; i++)
9604   {
9605     int xx = x + xy[i][0];
9606     int yy = y + xy[i][1];
9607
9608     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9609     {
9610       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9611
9612       break;
9613     }
9614   }
9615 }
9616
9617 static void InitTrap(int x, int y)
9618 {
9619   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9620 }
9621
9622 static void ActivateTrap(int x, int y)
9623 {
9624   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9625 }
9626
9627 static void ChangeActiveTrap(int x, int y)
9628 {
9629   int graphic = IMG_TRAP_ACTIVE;
9630
9631   /* if new animation frame was drawn, correct crumbled sand border */
9632   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9633     DrawLevelFieldCrumbledSand(x, y);
9634 }
9635
9636 static int getSpecialActionElement(int element, int number, int base_element)
9637 {
9638   return (element != EL_EMPTY ? element :
9639           number != -1 ? base_element + number - 1 :
9640           EL_EMPTY);
9641 }
9642
9643 static int getModifiedActionNumber(int value_old, int operator, int operand,
9644                                    int value_min, int value_max)
9645 {
9646   int value_new = (operator == CA_MODE_SET      ? operand :
9647                    operator == CA_MODE_ADD      ? value_old + operand :
9648                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9649                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9650                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9651                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9652                    value_old);
9653
9654   return (value_new < value_min ? value_min :
9655           value_new > value_max ? value_max :
9656           value_new);
9657 }
9658
9659 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9660 {
9661   struct ElementInfo *ei = &element_info[element];
9662   struct ElementChangeInfo *change = &ei->change_page[page];
9663   int target_element = change->target_element;
9664   int action_type = change->action_type;
9665   int action_mode = change->action_mode;
9666   int action_arg = change->action_arg;
9667   int i;
9668
9669   if (!change->has_action)
9670     return;
9671
9672   /* ---------- determine action paramater values -------------------------- */
9673
9674   int level_time_value =
9675     (level.time > 0 ? TimeLeft :
9676      TimePlayed);
9677
9678   int action_arg_element =
9679     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9680      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9681      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9682      EL_EMPTY);
9683
9684   int action_arg_direction =
9685     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9686      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9687      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9688      change->actual_trigger_side :
9689      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9690      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9691      MV_NONE);
9692
9693   int action_arg_number_min =
9694     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9695      CA_ARG_MIN);
9696
9697   int action_arg_number_max =
9698     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9699      action_type == CA_SET_LEVEL_GEMS ? 999 :
9700      action_type == CA_SET_LEVEL_TIME ? 9999 :
9701      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9702      action_type == CA_SET_CE_VALUE ? 9999 :
9703      action_type == CA_SET_CE_SCORE ? 9999 :
9704      CA_ARG_MAX);
9705
9706   int action_arg_number_reset =
9707     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9708      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9709      action_type == CA_SET_LEVEL_TIME ? level.time :
9710      action_type == CA_SET_LEVEL_SCORE ? 0 :
9711 #if USE_NEW_CUSTOM_VALUE
9712      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9713 #else
9714      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9715 #endif
9716      action_type == CA_SET_CE_SCORE ? 0 :
9717      0);
9718
9719   int action_arg_number =
9720     (action_arg <= CA_ARG_MAX ? action_arg :
9721      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9722      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9723      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9724      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9725      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9726      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9727 #if USE_NEW_CUSTOM_VALUE
9728      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9729 #else
9730      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9731 #endif
9732      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9733      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9734      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9735      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9736      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9737      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9738      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9739      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9740      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9741      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9742      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9743      -1);
9744
9745   int action_arg_number_old =
9746     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9747      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9748      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9749      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9750      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9751      0);
9752
9753   int action_arg_number_new =
9754     getModifiedActionNumber(action_arg_number_old,
9755                             action_mode, action_arg_number,
9756                             action_arg_number_min, action_arg_number_max);
9757
9758   int trigger_player_bits =
9759     (change->actual_trigger_player >= EL_PLAYER_1 &&
9760      change->actual_trigger_player <= EL_PLAYER_4 ?
9761      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9762      PLAYER_BITS_ANY);
9763
9764   int action_arg_player_bits =
9765     (action_arg >= CA_ARG_PLAYER_1 &&
9766      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9767      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9768      PLAYER_BITS_ANY);
9769
9770   /* ---------- execute action  -------------------------------------------- */
9771
9772   switch (action_type)
9773   {
9774     case CA_NO_ACTION:
9775     {
9776       return;
9777     }
9778
9779     /* ---------- level actions  ------------------------------------------- */
9780
9781     case CA_RESTART_LEVEL:
9782     {
9783       game.restart_level = TRUE;
9784
9785       break;
9786     }
9787
9788     case CA_SHOW_ENVELOPE:
9789     {
9790       int element = getSpecialActionElement(action_arg_element,
9791                                             action_arg_number, EL_ENVELOPE_1);
9792
9793       if (IS_ENVELOPE(element))
9794         local_player->show_envelope = element;
9795
9796       break;
9797     }
9798
9799     case CA_SET_LEVEL_TIME:
9800     {
9801       if (level.time > 0)       /* only modify limited time value */
9802       {
9803         TimeLeft = action_arg_number_new;
9804
9805 #if 1
9806         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9807
9808         DisplayGameControlValues();
9809 #else
9810         DrawGameValue_Time(TimeLeft);
9811 #endif
9812
9813         if (!TimeLeft && setup.time_limit)
9814           for (i = 0; i < MAX_PLAYERS; i++)
9815             KillPlayer(&stored_player[i]);
9816       }
9817
9818       break;
9819     }
9820
9821     case CA_SET_LEVEL_SCORE:
9822     {
9823       local_player->score = action_arg_number_new;
9824
9825 #if 1
9826       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9827
9828       DisplayGameControlValues();
9829 #else
9830       DrawGameValue_Score(local_player->score);
9831 #endif
9832
9833       break;
9834     }
9835
9836     case CA_SET_LEVEL_GEMS:
9837     {
9838       local_player->gems_still_needed = action_arg_number_new;
9839
9840 #if 1
9841       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9842
9843       DisplayGameControlValues();
9844 #else
9845       DrawGameValue_Emeralds(local_player->gems_still_needed);
9846 #endif
9847
9848       break;
9849     }
9850
9851 #if !USE_PLAYER_GRAVITY
9852     case CA_SET_LEVEL_GRAVITY:
9853     {
9854       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9855                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9856                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9857                       game.gravity);
9858       break;
9859     }
9860 #endif
9861
9862     case CA_SET_LEVEL_WIND:
9863     {
9864       game.wind_direction = action_arg_direction;
9865
9866       break;
9867     }
9868
9869     /* ---------- player actions  ------------------------------------------ */
9870
9871     case CA_MOVE_PLAYER:
9872     {
9873       /* automatically move to the next field in specified direction */
9874       for (i = 0; i < MAX_PLAYERS; i++)
9875         if (trigger_player_bits & (1 << i))
9876           stored_player[i].programmed_action = action_arg_direction;
9877
9878       break;
9879     }
9880
9881     case CA_EXIT_PLAYER:
9882     {
9883       for (i = 0; i < MAX_PLAYERS; i++)
9884         if (action_arg_player_bits & (1 << i))
9885           PlayerWins(&stored_player[i]);
9886
9887       break;
9888     }
9889
9890     case CA_KILL_PLAYER:
9891     {
9892       for (i = 0; i < MAX_PLAYERS; i++)
9893         if (action_arg_player_bits & (1 << i))
9894           KillPlayer(&stored_player[i]);
9895
9896       break;
9897     }
9898
9899     case CA_SET_PLAYER_KEYS:
9900     {
9901       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9902       int element = getSpecialActionElement(action_arg_element,
9903                                             action_arg_number, EL_KEY_1);
9904
9905       if (IS_KEY(element))
9906       {
9907         for (i = 0; i < MAX_PLAYERS; i++)
9908         {
9909           if (trigger_player_bits & (1 << i))
9910           {
9911             stored_player[i].key[KEY_NR(element)] = key_state;
9912
9913             DrawGameDoorValues();
9914           }
9915         }
9916       }
9917
9918       break;
9919     }
9920
9921     case CA_SET_PLAYER_SPEED:
9922     {
9923       for (i = 0; i < MAX_PLAYERS; i++)
9924       {
9925         if (trigger_player_bits & (1 << i))
9926         {
9927           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9928
9929           if (action_arg == CA_ARG_SPEED_FASTER &&
9930               stored_player[i].cannot_move)
9931           {
9932             action_arg_number = STEPSIZE_VERY_SLOW;
9933           }
9934           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9935                    action_arg == CA_ARG_SPEED_FASTER)
9936           {
9937             action_arg_number = 2;
9938             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9939                            CA_MODE_MULTIPLY);
9940           }
9941           else if (action_arg == CA_ARG_NUMBER_RESET)
9942           {
9943             action_arg_number = level.initial_player_stepsize[i];
9944           }
9945
9946           move_stepsize =
9947             getModifiedActionNumber(move_stepsize,
9948                                     action_mode,
9949                                     action_arg_number,
9950                                     action_arg_number_min,
9951                                     action_arg_number_max);
9952
9953           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9954         }
9955       }
9956
9957       break;
9958     }
9959
9960     case CA_SET_PLAYER_SHIELD:
9961     {
9962       for (i = 0; i < MAX_PLAYERS; i++)
9963       {
9964         if (trigger_player_bits & (1 << i))
9965         {
9966           if (action_arg == CA_ARG_SHIELD_OFF)
9967           {
9968             stored_player[i].shield_normal_time_left = 0;
9969             stored_player[i].shield_deadly_time_left = 0;
9970           }
9971           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9972           {
9973             stored_player[i].shield_normal_time_left = 999999;
9974           }
9975           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9976           {
9977             stored_player[i].shield_normal_time_left = 999999;
9978             stored_player[i].shield_deadly_time_left = 999999;
9979           }
9980         }
9981       }
9982
9983       break;
9984     }
9985
9986 #if USE_PLAYER_GRAVITY
9987     case CA_SET_PLAYER_GRAVITY:
9988     {
9989       for (i = 0; i < MAX_PLAYERS; i++)
9990       {
9991         if (trigger_player_bits & (1 << i))
9992         {
9993           stored_player[i].gravity =
9994             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9995              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9996              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9997              stored_player[i].gravity);
9998         }
9999       }
10000
10001       break;
10002     }
10003 #endif
10004
10005     case CA_SET_PLAYER_ARTWORK:
10006     {
10007       for (i = 0; i < MAX_PLAYERS; i++)
10008       {
10009         if (trigger_player_bits & (1 << i))
10010         {
10011           int artwork_element = action_arg_element;
10012
10013           if (action_arg == CA_ARG_ELEMENT_RESET)
10014             artwork_element =
10015               (level.use_artwork_element[i] ? level.artwork_element[i] :
10016                stored_player[i].element_nr);
10017
10018 #if USE_GFX_RESET_PLAYER_ARTWORK
10019           if (stored_player[i].artwork_element != artwork_element)
10020             stored_player[i].Frame = 0;
10021 #endif
10022
10023           stored_player[i].artwork_element = artwork_element;
10024
10025           SetPlayerWaiting(&stored_player[i], FALSE);
10026
10027           /* set number of special actions for bored and sleeping animation */
10028           stored_player[i].num_special_action_bored =
10029             get_num_special_action(artwork_element,
10030                                    ACTION_BORING_1, ACTION_BORING_LAST);
10031           stored_player[i].num_special_action_sleeping =
10032             get_num_special_action(artwork_element,
10033                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10034         }
10035       }
10036
10037       break;
10038     }
10039
10040     /* ---------- CE actions  ---------------------------------------------- */
10041
10042     case CA_SET_CE_VALUE:
10043     {
10044 #if USE_NEW_CUSTOM_VALUE
10045       int last_ce_value = CustomValue[x][y];
10046
10047       CustomValue[x][y] = action_arg_number_new;
10048
10049       if (CustomValue[x][y] != last_ce_value)
10050       {
10051         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10052         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10053
10054         if (CustomValue[x][y] == 0)
10055         {
10056           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10057           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10058         }
10059       }
10060 #endif
10061
10062       break;
10063     }
10064
10065     case CA_SET_CE_SCORE:
10066     {
10067 #if USE_NEW_CUSTOM_VALUE
10068       int last_ce_score = ei->collect_score;
10069
10070       ei->collect_score = action_arg_number_new;
10071
10072       if (ei->collect_score != last_ce_score)
10073       {
10074         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10075         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10076
10077         if (ei->collect_score == 0)
10078         {
10079           int xx, yy;
10080
10081           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10082           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10083
10084           /*
10085             This is a very special case that seems to be a mixture between
10086             CheckElementChange() and CheckTriggeredElementChange(): while
10087             the first one only affects single elements that are triggered
10088             directly, the second one affects multiple elements in the playfield
10089             that are triggered indirectly by another element. This is a third
10090             case: Changing the CE score always affects multiple identical CEs,
10091             so every affected CE must be checked, not only the single CE for
10092             which the CE score was changed in the first place (as every instance
10093             of that CE shares the same CE score, and therefore also can change)!
10094           */
10095           SCAN_PLAYFIELD(xx, yy)
10096           {
10097             if (Feld[xx][yy] == element)
10098               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10099                                  CE_SCORE_GETS_ZERO);
10100           }
10101         }
10102       }
10103 #endif
10104
10105       break;
10106     }
10107
10108     /* ---------- engine actions  ------------------------------------------ */
10109
10110     case CA_SET_ENGINE_SCAN_MODE:
10111     {
10112       InitPlayfieldScanMode(action_arg);
10113
10114       break;
10115     }
10116
10117     default:
10118       break;
10119   }
10120 }
10121
10122 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10123 {
10124   int old_element = Feld[x][y];
10125   int new_element = GetElementFromGroupElement(element);
10126   int previous_move_direction = MovDir[x][y];
10127 #if USE_NEW_CUSTOM_VALUE
10128   int last_ce_value = CustomValue[x][y];
10129 #endif
10130   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10131   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10132   boolean add_player_onto_element = (new_element_is_player &&
10133 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10134                                      /* this breaks SnakeBite when a snake is
10135                                         halfway through a door that closes */
10136                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10137                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10138 #endif
10139                                      IS_WALKABLE(old_element));
10140
10141 #if 0
10142   /* check if element under the player changes from accessible to unaccessible
10143      (needed for special case of dropping element which then changes) */
10144   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10145       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10146   {
10147     Bang(x, y);
10148
10149     return;
10150   }
10151 #endif
10152
10153   if (!add_player_onto_element)
10154   {
10155     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10156       RemoveMovingField(x, y);
10157     else
10158       RemoveField(x, y);
10159
10160     Feld[x][y] = new_element;
10161
10162 #if !USE_GFX_RESET_GFX_ANIMATION
10163     ResetGfxAnimation(x, y);
10164     ResetRandomAnimationValue(x, y);
10165 #endif
10166
10167     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10168       MovDir[x][y] = previous_move_direction;
10169
10170 #if USE_NEW_CUSTOM_VALUE
10171     if (element_info[new_element].use_last_ce_value)
10172       CustomValue[x][y] = last_ce_value;
10173 #endif
10174
10175     InitField_WithBug1(x, y, FALSE);
10176
10177     new_element = Feld[x][y];   /* element may have changed */
10178
10179 #if USE_GFX_RESET_GFX_ANIMATION
10180     ResetGfxAnimation(x, y);
10181     ResetRandomAnimationValue(x, y);
10182 #endif
10183
10184     DrawLevelField(x, y);
10185
10186     if (GFX_CRUMBLED(new_element))
10187       DrawLevelFieldCrumbledSandNeighbours(x, y);
10188   }
10189
10190 #if 1
10191   /* check if element under the player changes from accessible to unaccessible
10192      (needed for special case of dropping element which then changes) */
10193   /* (must be checked after creating new element for walkable group elements) */
10194 #if USE_FIX_KILLED_BY_NON_WALKABLE
10195   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10196       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10197   {
10198     Bang(x, y);
10199
10200     return;
10201   }
10202 #else
10203   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10204       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10205   {
10206     Bang(x, y);
10207
10208     return;
10209   }
10210 #endif
10211 #endif
10212
10213   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10214   if (new_element_is_player)
10215     RelocatePlayer(x, y, new_element);
10216
10217   if (is_change)
10218     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10219
10220   TestIfBadThingTouchesPlayer(x, y);
10221   TestIfPlayerTouchesCustomElement(x, y);
10222   TestIfElementTouchesCustomElement(x, y);
10223 }
10224
10225 static void CreateField(int x, int y, int element)
10226 {
10227   CreateFieldExt(x, y, element, FALSE);
10228 }
10229
10230 static void CreateElementFromChange(int x, int y, int element)
10231 {
10232   element = GET_VALID_RUNTIME_ELEMENT(element);
10233
10234 #if USE_STOP_CHANGED_ELEMENTS
10235   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10236   {
10237     int old_element = Feld[x][y];
10238
10239     /* prevent changed element from moving in same engine frame
10240        unless both old and new element can either fall or move */
10241     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10242         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10243       Stop[x][y] = TRUE;
10244   }
10245 #endif
10246
10247   CreateFieldExt(x, y, element, TRUE);
10248 }
10249
10250 static boolean ChangeElement(int x, int y, int element, int page)
10251 {
10252   struct ElementInfo *ei = &element_info[element];
10253   struct ElementChangeInfo *change = &ei->change_page[page];
10254   int ce_value = CustomValue[x][y];
10255   int ce_score = ei->collect_score;
10256   int target_element;
10257   int old_element = Feld[x][y];
10258
10259   /* always use default change event to prevent running into a loop */
10260   if (ChangeEvent[x][y] == -1)
10261     ChangeEvent[x][y] = CE_DELAY;
10262
10263   if (ChangeEvent[x][y] == CE_DELAY)
10264   {
10265     /* reset actual trigger element, trigger player and action element */
10266     change->actual_trigger_element = EL_EMPTY;
10267     change->actual_trigger_player = EL_PLAYER_1;
10268     change->actual_trigger_side = CH_SIDE_NONE;
10269     change->actual_trigger_ce_value = 0;
10270     change->actual_trigger_ce_score = 0;
10271   }
10272
10273   /* do not change elements more than a specified maximum number of changes */
10274   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10275     return FALSE;
10276
10277   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10278
10279   if (change->explode)
10280   {
10281     Bang(x, y);
10282
10283     return TRUE;
10284   }
10285
10286   if (change->use_target_content)
10287   {
10288     boolean complete_replace = TRUE;
10289     boolean can_replace[3][3];
10290     int xx, yy;
10291
10292     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10293     {
10294       boolean is_empty;
10295       boolean is_walkable;
10296       boolean is_diggable;
10297       boolean is_collectible;
10298       boolean is_removable;
10299       boolean is_destructible;
10300       int ex = x + xx - 1;
10301       int ey = y + yy - 1;
10302       int content_element = change->target_content.e[xx][yy];
10303       int e;
10304
10305       can_replace[xx][yy] = TRUE;
10306
10307       if (ex == x && ey == y)   /* do not check changing element itself */
10308         continue;
10309
10310       if (content_element == EL_EMPTY_SPACE)
10311       {
10312         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10313
10314         continue;
10315       }
10316
10317       if (!IN_LEV_FIELD(ex, ey))
10318       {
10319         can_replace[xx][yy] = FALSE;
10320         complete_replace = FALSE;
10321
10322         continue;
10323       }
10324
10325       e = Feld[ex][ey];
10326
10327       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10328         e = MovingOrBlocked2Element(ex, ey);
10329
10330       is_empty = (IS_FREE(ex, ey) ||
10331                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10332
10333       is_walkable     = (is_empty || IS_WALKABLE(e));
10334       is_diggable     = (is_empty || IS_DIGGABLE(e));
10335       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10336       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10337       is_removable    = (is_diggable || is_collectible);
10338
10339       can_replace[xx][yy] =
10340         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10341           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10342           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10343           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10344           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10345           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10346          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10347
10348       if (!can_replace[xx][yy])
10349         complete_replace = FALSE;
10350     }
10351
10352     if (!change->only_if_complete || complete_replace)
10353     {
10354       boolean something_has_changed = FALSE;
10355
10356       if (change->only_if_complete && change->use_random_replace &&
10357           RND(100) < change->random_percentage)
10358         return FALSE;
10359
10360       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10361       {
10362         int ex = x + xx - 1;
10363         int ey = y + yy - 1;
10364         int content_element;
10365
10366         if (can_replace[xx][yy] && (!change->use_random_replace ||
10367                                     RND(100) < change->random_percentage))
10368         {
10369           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10370             RemoveMovingField(ex, ey);
10371
10372           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10373
10374           content_element = change->target_content.e[xx][yy];
10375           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10376                                               ce_value, ce_score);
10377
10378           CreateElementFromChange(ex, ey, target_element);
10379
10380           something_has_changed = TRUE;
10381
10382           /* for symmetry reasons, freeze newly created border elements */
10383           if (ex != x || ey != y)
10384             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10385         }
10386       }
10387
10388       if (something_has_changed)
10389       {
10390         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10391         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10392       }
10393     }
10394   }
10395   else
10396   {
10397     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10398                                         ce_value, ce_score);
10399
10400     if (element == EL_DIAGONAL_GROWING ||
10401         element == EL_DIAGONAL_SHRINKING)
10402     {
10403       target_element = Store[x][y];
10404
10405       Store[x][y] = EL_EMPTY;
10406     }
10407
10408     CreateElementFromChange(x, y, target_element);
10409
10410     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10411     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10412   }
10413
10414   /* this uses direct change before indirect change */
10415   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10416
10417   return TRUE;
10418 }
10419
10420 #if USE_NEW_DELAYED_ACTION
10421
10422 static void HandleElementChange(int x, int y, int page)
10423 {
10424   int element = MovingOrBlocked2Element(x, y);
10425   struct ElementInfo *ei = &element_info[element];
10426   struct ElementChangeInfo *change = &ei->change_page[page];
10427
10428 #ifdef DEBUG
10429   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10430       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10431   {
10432     printf("\n\n");
10433     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10434            x, y, element, element_info[element].token_name);
10435     printf("HandleElementChange(): This should never happen!\n");
10436     printf("\n\n");
10437   }
10438 #endif
10439
10440   /* this can happen with classic bombs on walkable, changing elements */
10441   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10442   {
10443 #if 0
10444     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10445       ChangeDelay[x][y] = 0;
10446 #endif
10447
10448     return;
10449   }
10450
10451   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10452   {
10453     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10454
10455     if (change->can_change)
10456     {
10457 #if 1
10458       /* !!! not clear why graphic animation should be reset at all here !!! */
10459       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10460 #if USE_GFX_RESET_WHEN_NOT_MOVING
10461       /* when a custom element is about to change (for example by change delay),
10462          do not reset graphic animation when the custom element is moving */
10463       if (!IS_MOVING(x, y))
10464 #endif
10465       {
10466         ResetGfxAnimation(x, y);
10467         ResetRandomAnimationValue(x, y);
10468       }
10469 #endif
10470
10471       if (change->pre_change_function)
10472         change->pre_change_function(x, y);
10473     }
10474   }
10475
10476   ChangeDelay[x][y]--;
10477
10478   if (ChangeDelay[x][y] != 0)           /* continue element change */
10479   {
10480     if (change->can_change)
10481     {
10482       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10483
10484       if (IS_ANIMATED(graphic))
10485         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10486
10487       if (change->change_function)
10488         change->change_function(x, y);
10489     }
10490   }
10491   else                                  /* finish element change */
10492   {
10493     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10494     {
10495       page = ChangePage[x][y];
10496       ChangePage[x][y] = -1;
10497
10498       change = &ei->change_page[page];
10499     }
10500
10501     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10502     {
10503       ChangeDelay[x][y] = 1;            /* try change after next move step */
10504       ChangePage[x][y] = page;          /* remember page to use for change */
10505
10506       return;
10507     }
10508
10509     if (change->can_change)
10510     {
10511       if (ChangeElement(x, y, element, page))
10512       {
10513         if (change->post_change_function)
10514           change->post_change_function(x, y);
10515       }
10516     }
10517
10518     if (change->has_action)
10519       ExecuteCustomElementAction(x, y, element, page);
10520   }
10521 }
10522
10523 #else
10524
10525 static void HandleElementChange(int x, int y, int page)
10526 {
10527   int element = MovingOrBlocked2Element(x, y);
10528   struct ElementInfo *ei = &element_info[element];
10529   struct ElementChangeInfo *change = &ei->change_page[page];
10530
10531 #ifdef DEBUG
10532   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10533   {
10534     printf("\n\n");
10535     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10536            x, y, element, element_info[element].token_name);
10537     printf("HandleElementChange(): This should never happen!\n");
10538     printf("\n\n");
10539   }
10540 #endif
10541
10542   /* this can happen with classic bombs on walkable, changing elements */
10543   if (!CAN_CHANGE(element))
10544   {
10545 #if 0
10546     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10547       ChangeDelay[x][y] = 0;
10548 #endif
10549
10550     return;
10551   }
10552
10553   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10554   {
10555     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10556
10557     ResetGfxAnimation(x, y);
10558     ResetRandomAnimationValue(x, y);
10559
10560     if (change->pre_change_function)
10561       change->pre_change_function(x, y);
10562   }
10563
10564   ChangeDelay[x][y]--;
10565
10566   if (ChangeDelay[x][y] != 0)           /* continue element change */
10567   {
10568     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10569
10570     if (IS_ANIMATED(graphic))
10571       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10572
10573     if (change->change_function)
10574       change->change_function(x, y);
10575   }
10576   else                                  /* finish element change */
10577   {
10578     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10579     {
10580       page = ChangePage[x][y];
10581       ChangePage[x][y] = -1;
10582
10583       change = &ei->change_page[page];
10584     }
10585
10586     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10587     {
10588       ChangeDelay[x][y] = 1;            /* try change after next move step */
10589       ChangePage[x][y] = page;          /* remember page to use for change */
10590
10591       return;
10592     }
10593
10594     if (ChangeElement(x, y, element, page))
10595     {
10596       if (change->post_change_function)
10597         change->post_change_function(x, y);
10598     }
10599   }
10600 }
10601
10602 #endif
10603
10604 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10605                                               int trigger_element,
10606                                               int trigger_event,
10607                                               int trigger_player,
10608                                               int trigger_side,
10609                                               int trigger_page)
10610 {
10611   boolean change_done_any = FALSE;
10612   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10613   int i;
10614
10615   if (!(trigger_events[trigger_element][trigger_event]))
10616     return FALSE;
10617
10618 #if 0
10619   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10620          trigger_event, recursion_loop_depth, recursion_loop_detected,
10621          recursion_loop_element, EL_NAME(recursion_loop_element));
10622 #endif
10623
10624   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10625
10626   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10627   {
10628     int element = EL_CUSTOM_START + i;
10629     boolean change_done = FALSE;
10630     int p;
10631
10632     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10633         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10634       continue;
10635
10636     for (p = 0; p < element_info[element].num_change_pages; p++)
10637     {
10638       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10639
10640       if (change->can_change_or_has_action &&
10641           change->has_event[trigger_event] &&
10642           change->trigger_side & trigger_side &&
10643           change->trigger_player & trigger_player &&
10644           change->trigger_page & trigger_page_bits &&
10645           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10646       {
10647         change->actual_trigger_element = trigger_element;
10648         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10649         change->actual_trigger_side = trigger_side;
10650         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10651         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10652
10653         if ((change->can_change && !change_done) || change->has_action)
10654         {
10655           int x, y;
10656
10657           SCAN_PLAYFIELD(x, y)
10658           {
10659             if (Feld[x][y] == element)
10660             {
10661               if (change->can_change && !change_done)
10662               {
10663                 ChangeDelay[x][y] = 1;
10664                 ChangeEvent[x][y] = trigger_event;
10665
10666                 HandleElementChange(x, y, p);
10667               }
10668 #if USE_NEW_DELAYED_ACTION
10669               else if (change->has_action)
10670               {
10671                 ExecuteCustomElementAction(x, y, element, p);
10672                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10673               }
10674 #else
10675               if (change->has_action)
10676               {
10677                 ExecuteCustomElementAction(x, y, element, p);
10678                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10679               }
10680 #endif
10681             }
10682           }
10683
10684           if (change->can_change)
10685           {
10686             change_done = TRUE;
10687             change_done_any = TRUE;
10688           }
10689         }
10690       }
10691     }
10692   }
10693
10694   RECURSION_LOOP_DETECTION_END();
10695
10696   return change_done_any;
10697 }
10698
10699 static boolean CheckElementChangeExt(int x, int y,
10700                                      int element,
10701                                      int trigger_element,
10702                                      int trigger_event,
10703                                      int trigger_player,
10704                                      int trigger_side)
10705 {
10706   boolean change_done = FALSE;
10707   int p;
10708
10709   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10710       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10711     return FALSE;
10712
10713   if (Feld[x][y] == EL_BLOCKED)
10714   {
10715     Blocked2Moving(x, y, &x, &y);
10716     element = Feld[x][y];
10717   }
10718
10719 #if 0
10720   /* check if element has already changed */
10721   if (Feld[x][y] != element)
10722     return FALSE;
10723 #else
10724   /* check if element has already changed or is about to change after moving */
10725   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10726        Feld[x][y] != element) ||
10727
10728       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10729        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10730         ChangePage[x][y] != -1)))
10731     return FALSE;
10732 #endif
10733
10734 #if 0
10735   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10736          trigger_event, recursion_loop_depth, recursion_loop_detected,
10737          recursion_loop_element, EL_NAME(recursion_loop_element));
10738 #endif
10739
10740   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10741
10742   for (p = 0; p < element_info[element].num_change_pages; p++)
10743   {
10744     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10745
10746     /* check trigger element for all events where the element that is checked
10747        for changing interacts with a directly adjacent element -- this is
10748        different to element changes that affect other elements to change on the
10749        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10750     boolean check_trigger_element =
10751       (trigger_event == CE_TOUCHING_X ||
10752        trigger_event == CE_HITTING_X ||
10753        trigger_event == CE_HIT_BY_X ||
10754 #if 1
10755        /* this one was forgotten until 3.2.3 */
10756        trigger_event == CE_DIGGING_X);
10757 #endif
10758
10759     if (change->can_change_or_has_action &&
10760         change->has_event[trigger_event] &&
10761         change->trigger_side & trigger_side &&
10762         change->trigger_player & trigger_player &&
10763         (!check_trigger_element ||
10764          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10765     {
10766       change->actual_trigger_element = trigger_element;
10767       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10768       change->actual_trigger_side = trigger_side;
10769       change->actual_trigger_ce_value = CustomValue[x][y];
10770       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10771
10772       /* special case: trigger element not at (x,y) position for some events */
10773       if (check_trigger_element)
10774       {
10775         static struct
10776         {
10777           int dx, dy;
10778         } move_xy[] =
10779           {
10780             {  0,  0 },
10781             { -1,  0 },
10782             { +1,  0 },
10783             {  0,  0 },
10784             {  0, -1 },
10785             {  0,  0 }, { 0, 0 }, { 0, 0 },
10786             {  0, +1 }
10787           };
10788
10789         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10790         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10791
10792         change->actual_trigger_ce_value = CustomValue[xx][yy];
10793         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10794       }
10795
10796       if (change->can_change && !change_done)
10797       {
10798         ChangeDelay[x][y] = 1;
10799         ChangeEvent[x][y] = trigger_event;
10800
10801         HandleElementChange(x, y, p);
10802
10803         change_done = TRUE;
10804       }
10805 #if USE_NEW_DELAYED_ACTION
10806       else if (change->has_action)
10807       {
10808         ExecuteCustomElementAction(x, y, element, p);
10809         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10810       }
10811 #else
10812       if (change->has_action)
10813       {
10814         ExecuteCustomElementAction(x, y, element, p);
10815         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10816       }
10817 #endif
10818     }
10819   }
10820
10821   RECURSION_LOOP_DETECTION_END();
10822
10823   return change_done;
10824 }
10825
10826 static void PlayPlayerSound(struct PlayerInfo *player)
10827 {
10828   int jx = player->jx, jy = player->jy;
10829   int sound_element = player->artwork_element;
10830   int last_action = player->last_action_waiting;
10831   int action = player->action_waiting;
10832
10833   if (player->is_waiting)
10834   {
10835     if (action != last_action)
10836       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10837     else
10838       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10839   }
10840   else
10841   {
10842     if (action != last_action)
10843       StopSound(element_info[sound_element].sound[last_action]);
10844
10845     if (last_action == ACTION_SLEEPING)
10846       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10847   }
10848 }
10849
10850 static void PlayAllPlayersSound()
10851 {
10852   int i;
10853
10854   for (i = 0; i < MAX_PLAYERS; i++)
10855     if (stored_player[i].active)
10856       PlayPlayerSound(&stored_player[i]);
10857 }
10858
10859 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10860 {
10861   boolean last_waiting = player->is_waiting;
10862   int move_dir = player->MovDir;
10863
10864   player->dir_waiting = move_dir;
10865   player->last_action_waiting = player->action_waiting;
10866
10867   if (is_waiting)
10868   {
10869     if (!last_waiting)          /* not waiting -> waiting */
10870     {
10871       player->is_waiting = TRUE;
10872
10873       player->frame_counter_bored =
10874         FrameCounter +
10875         game.player_boring_delay_fixed +
10876         GetSimpleRandom(game.player_boring_delay_random);
10877       player->frame_counter_sleeping =
10878         FrameCounter +
10879         game.player_sleeping_delay_fixed +
10880         GetSimpleRandom(game.player_sleeping_delay_random);
10881
10882       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10883     }
10884
10885     if (game.player_sleeping_delay_fixed +
10886         game.player_sleeping_delay_random > 0 &&
10887         player->anim_delay_counter == 0 &&
10888         player->post_delay_counter == 0 &&
10889         FrameCounter >= player->frame_counter_sleeping)
10890       player->is_sleeping = TRUE;
10891     else if (game.player_boring_delay_fixed +
10892              game.player_boring_delay_random > 0 &&
10893              FrameCounter >= player->frame_counter_bored)
10894       player->is_bored = TRUE;
10895
10896     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10897                               player->is_bored ? ACTION_BORING :
10898                               ACTION_WAITING);
10899
10900     if (player->is_sleeping && player->use_murphy)
10901     {
10902       /* special case for sleeping Murphy when leaning against non-free tile */
10903
10904       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10905           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10906            !IS_MOVING(player->jx - 1, player->jy)))
10907         move_dir = MV_LEFT;
10908       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10909                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10910                 !IS_MOVING(player->jx + 1, player->jy)))
10911         move_dir = MV_RIGHT;
10912       else
10913         player->is_sleeping = FALSE;
10914
10915       player->dir_waiting = move_dir;
10916     }
10917
10918     if (player->is_sleeping)
10919     {
10920       if (player->num_special_action_sleeping > 0)
10921       {
10922         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10923         {
10924           int last_special_action = player->special_action_sleeping;
10925           int num_special_action = player->num_special_action_sleeping;
10926           int special_action =
10927             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10928              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10929              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10930              last_special_action + 1 : ACTION_SLEEPING);
10931           int special_graphic =
10932             el_act_dir2img(player->artwork_element, special_action, move_dir);
10933
10934           player->anim_delay_counter =
10935             graphic_info[special_graphic].anim_delay_fixed +
10936             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10937           player->post_delay_counter =
10938             graphic_info[special_graphic].post_delay_fixed +
10939             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10940
10941           player->special_action_sleeping = special_action;
10942         }
10943
10944         if (player->anim_delay_counter > 0)
10945         {
10946           player->action_waiting = player->special_action_sleeping;
10947           player->anim_delay_counter--;
10948         }
10949         else if (player->post_delay_counter > 0)
10950         {
10951           player->post_delay_counter--;
10952         }
10953       }
10954     }
10955     else if (player->is_bored)
10956     {
10957       if (player->num_special_action_bored > 0)
10958       {
10959         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10960         {
10961           int special_action =
10962             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10963           int special_graphic =
10964             el_act_dir2img(player->artwork_element, special_action, move_dir);
10965
10966           player->anim_delay_counter =
10967             graphic_info[special_graphic].anim_delay_fixed +
10968             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10969           player->post_delay_counter =
10970             graphic_info[special_graphic].post_delay_fixed +
10971             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10972
10973           player->special_action_bored = special_action;
10974         }
10975
10976         if (player->anim_delay_counter > 0)
10977         {
10978           player->action_waiting = player->special_action_bored;
10979           player->anim_delay_counter--;
10980         }
10981         else if (player->post_delay_counter > 0)
10982         {
10983           player->post_delay_counter--;
10984         }
10985       }
10986     }
10987   }
10988   else if (last_waiting)        /* waiting -> not waiting */
10989   {
10990     player->is_waiting = FALSE;
10991     player->is_bored = FALSE;
10992     player->is_sleeping = FALSE;
10993
10994     player->frame_counter_bored = -1;
10995     player->frame_counter_sleeping = -1;
10996
10997     player->anim_delay_counter = 0;
10998     player->post_delay_counter = 0;
10999
11000     player->dir_waiting = player->MovDir;
11001     player->action_waiting = ACTION_DEFAULT;
11002
11003     player->special_action_bored = ACTION_DEFAULT;
11004     player->special_action_sleeping = ACTION_DEFAULT;
11005   }
11006 }
11007
11008 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11009 {
11010   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11011   int left      = player_action & JOY_LEFT;
11012   int right     = player_action & JOY_RIGHT;
11013   int up        = player_action & JOY_UP;
11014   int down      = player_action & JOY_DOWN;
11015   int button1   = player_action & JOY_BUTTON_1;
11016   int button2   = player_action & JOY_BUTTON_2;
11017   int dx        = (left ? -1 : right ? 1 : 0);
11018   int dy        = (up   ? -1 : down  ? 1 : 0);
11019
11020   if (!player->active || tape.pausing)
11021     return 0;
11022
11023   if (player_action)
11024   {
11025     if (button1)
11026       snapped = SnapField(player, dx, dy);
11027     else
11028     {
11029       if (button2)
11030         dropped = DropElement(player);
11031
11032       moved = MovePlayer(player, dx, dy);
11033     }
11034
11035     if (tape.single_step && tape.recording && !tape.pausing)
11036     {
11037       if (button1 || (dropped && !moved))
11038       {
11039         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11040         SnapField(player, 0, 0);                /* stop snapping */
11041       }
11042     }
11043
11044     SetPlayerWaiting(player, FALSE);
11045
11046     return player_action;
11047   }
11048   else
11049   {
11050     /* no actions for this player (no input at player's configured device) */
11051
11052     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11053     SnapField(player, 0, 0);
11054     CheckGravityMovementWhenNotMoving(player);
11055
11056     if (player->MovPos == 0)
11057       SetPlayerWaiting(player, TRUE);
11058
11059     if (player->MovPos == 0)    /* needed for tape.playing */
11060       player->is_moving = FALSE;
11061
11062     player->is_dropping = FALSE;
11063     player->is_dropping_pressed = FALSE;
11064     player->drop_pressed_delay = 0;
11065
11066     return 0;
11067   }
11068 }
11069
11070 static void CheckLevelTime()
11071 {
11072   int i;
11073
11074   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11075   {
11076     if (level.native_em_level->lev->home == 0)  /* all players at home */
11077     {
11078       PlayerWins(local_player);
11079
11080       AllPlayersGone = TRUE;
11081
11082       level.native_em_level->lev->home = -1;
11083     }
11084
11085     if (level.native_em_level->ply[0]->alive == 0 &&
11086         level.native_em_level->ply[1]->alive == 0 &&
11087         level.native_em_level->ply[2]->alive == 0 &&
11088         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11089       AllPlayersGone = TRUE;
11090   }
11091
11092   if (TimeFrames >= FRAMES_PER_SECOND)
11093   {
11094     TimeFrames = 0;
11095     TapeTime++;
11096
11097     for (i = 0; i < MAX_PLAYERS; i++)
11098     {
11099       struct PlayerInfo *player = &stored_player[i];
11100
11101       if (SHIELD_ON(player))
11102       {
11103         player->shield_normal_time_left--;
11104
11105         if (player->shield_deadly_time_left > 0)
11106           player->shield_deadly_time_left--;
11107       }
11108     }
11109
11110     if (!local_player->LevelSolved && !level.use_step_counter)
11111     {
11112       TimePlayed++;
11113
11114       if (TimeLeft > 0)
11115       {
11116         TimeLeft--;
11117
11118         if (TimeLeft <= 10 && setup.time_limit)
11119           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11120
11121 #if 1
11122         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11123
11124         DisplayGameControlValues();
11125 #else
11126         DrawGameValue_Time(TimeLeft);
11127 #endif
11128
11129         if (!TimeLeft && setup.time_limit)
11130         {
11131           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11132             level.native_em_level->lev->killed_out_of_time = TRUE;
11133           else
11134             for (i = 0; i < MAX_PLAYERS; i++)
11135               KillPlayer(&stored_player[i]);
11136         }
11137       }
11138 #if 1
11139       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11140       {
11141         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11142
11143         DisplayGameControlValues();
11144       }
11145 #else
11146       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11147         DrawGameValue_Time(TimePlayed);
11148 #endif
11149
11150       level.native_em_level->lev->time =
11151         (level.time == 0 ? TimePlayed : TimeLeft);
11152     }
11153
11154     if (tape.recording || tape.playing)
11155       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11156   }
11157
11158   UpdateGameDoorValues();
11159   DrawGameDoorValues();
11160 }
11161
11162 void AdvanceFrameAndPlayerCounters(int player_nr)
11163 {
11164   int i;
11165
11166   /* advance frame counters (global frame counter and time frame counter) */
11167   FrameCounter++;
11168   TimeFrames++;
11169
11170   /* advance player counters (counters for move delay, move animation etc.) */
11171   for (i = 0; i < MAX_PLAYERS; i++)
11172   {
11173     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11174     int move_delay_value = stored_player[i].move_delay_value;
11175     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11176
11177     if (!advance_player_counters)       /* not all players may be affected */
11178       continue;
11179
11180 #if USE_NEW_PLAYER_ANIM
11181     if (move_frames == 0)       /* less than one move per game frame */
11182     {
11183       int stepsize = TILEX / move_delay_value;
11184       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11185       int count = (stored_player[i].is_moving ?
11186                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11187
11188       if (count % delay == 0)
11189         move_frames = 1;
11190     }
11191 #endif
11192
11193     stored_player[i].Frame += move_frames;
11194
11195     if (stored_player[i].MovPos != 0)
11196       stored_player[i].StepFrame += move_frames;
11197
11198     if (stored_player[i].move_delay > 0)
11199       stored_player[i].move_delay--;
11200
11201     /* due to bugs in previous versions, counter must count up, not down */
11202     if (stored_player[i].push_delay != -1)
11203       stored_player[i].push_delay++;
11204
11205     if (stored_player[i].drop_delay > 0)
11206       stored_player[i].drop_delay--;
11207
11208     if (stored_player[i].is_dropping_pressed)
11209       stored_player[i].drop_pressed_delay++;
11210   }
11211 }
11212
11213 void StartGameActions(boolean init_network_game, boolean record_tape,
11214                       long random_seed)
11215 {
11216   unsigned long new_random_seed = InitRND(random_seed);
11217
11218   if (record_tape)
11219     TapeStartRecording(new_random_seed);
11220
11221 #if defined(NETWORK_AVALIABLE)
11222   if (init_network_game)
11223   {
11224     SendToServer_StartPlaying();
11225
11226     return;
11227   }
11228 #endif
11229
11230   InitGame();
11231 }
11232
11233 void GameActions()
11234 {
11235   static unsigned long game_frame_delay = 0;
11236   unsigned long game_frame_delay_value;
11237   byte *recorded_player_action;
11238   byte summarized_player_action = 0;
11239   byte tape_action[MAX_PLAYERS];
11240   int i;
11241
11242   /* detect endless loops, caused by custom element programming */
11243   if (recursion_loop_detected && recursion_loop_depth == 0)
11244   {
11245     char *message = getStringCat3("Internal Error ! Element ",
11246                                   EL_NAME(recursion_loop_element),
11247                                   " caused endless loop ! Quit the game ?");
11248
11249     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11250           EL_NAME(recursion_loop_element));
11251
11252     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11253
11254     recursion_loop_detected = FALSE;    /* if game should be continued */
11255
11256     free(message);
11257
11258     return;
11259   }
11260
11261   if (game.restart_level)
11262     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11263
11264   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11265   {
11266     if (level.native_em_level->lev->home == 0)  /* all players at home */
11267     {
11268       PlayerWins(local_player);
11269
11270       AllPlayersGone = TRUE;
11271
11272       level.native_em_level->lev->home = -1;
11273     }
11274
11275     if (level.native_em_level->ply[0]->alive == 0 &&
11276         level.native_em_level->ply[1]->alive == 0 &&
11277         level.native_em_level->ply[2]->alive == 0 &&
11278         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11279       AllPlayersGone = TRUE;
11280   }
11281
11282   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11283     GameWon();
11284
11285   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11286     TapeStop();
11287
11288   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11289     return;
11290
11291   game_frame_delay_value =
11292     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11293
11294   if (tape.playing && tape.warp_forward && !tape.pausing)
11295     game_frame_delay_value = 0;
11296
11297   /* ---------- main game synchronization point ---------- */
11298
11299   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11300
11301   if (network_playing && !network_player_action_received)
11302   {
11303     /* try to get network player actions in time */
11304
11305 #if defined(NETWORK_AVALIABLE)
11306     /* last chance to get network player actions without main loop delay */
11307     HandleNetworking();
11308 #endif
11309
11310     /* game was quit by network peer */
11311     if (game_status != GAME_MODE_PLAYING)
11312       return;
11313
11314     if (!network_player_action_received)
11315       return;           /* failed to get network player actions in time */
11316
11317     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11318   }
11319
11320   if (tape.pausing)
11321     return;
11322
11323   /* at this point we know that we really continue executing the game */
11324
11325   network_player_action_received = FALSE;
11326
11327   /* when playing tape, read previously recorded player input from tape data */
11328   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11329
11330 #if 1
11331   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11332   if (tape.pausing)
11333     return;
11334 #endif
11335
11336   if (tape.set_centered_player)
11337   {
11338     game.centered_player_nr_next = tape.centered_player_nr_next;
11339     game.set_centered_player = TRUE;
11340   }
11341
11342   for (i = 0; i < MAX_PLAYERS; i++)
11343   {
11344     summarized_player_action |= stored_player[i].action;
11345
11346     if (!network_playing)
11347       stored_player[i].effective_action = stored_player[i].action;
11348   }
11349
11350 #if defined(NETWORK_AVALIABLE)
11351   if (network_playing)
11352     SendToServer_MovePlayer(summarized_player_action);
11353 #endif
11354
11355   if (!options.network && !setup.team_mode)
11356     local_player->effective_action = summarized_player_action;
11357
11358   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11359   {
11360     for (i = 0; i < MAX_PLAYERS; i++)
11361       stored_player[i].effective_action =
11362         (i == game.centered_player_nr ? summarized_player_action : 0);
11363   }
11364
11365   if (recorded_player_action != NULL)
11366     for (i = 0; i < MAX_PLAYERS; i++)
11367       stored_player[i].effective_action = recorded_player_action[i];
11368
11369   for (i = 0; i < MAX_PLAYERS; i++)
11370   {
11371     tape_action[i] = stored_player[i].effective_action;
11372
11373     /* (this can only happen in the R'n'D game engine) */
11374     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11375       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11376   }
11377
11378   /* only record actions from input devices, but not programmed actions */
11379   if (tape.recording)
11380     TapeRecordAction(tape_action);
11381
11382   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11383   {
11384     GameActions_EM_Main();
11385   }
11386   else
11387   {
11388     GameActions_RND();
11389   }
11390 }
11391
11392 void GameActions_EM_Main()
11393 {
11394   byte effective_action[MAX_PLAYERS];
11395   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11396   int i;
11397
11398   for (i = 0; i < MAX_PLAYERS; i++)
11399     effective_action[i] = stored_player[i].effective_action;
11400
11401   GameActions_EM(effective_action, warp_mode);
11402
11403   CheckLevelTime();
11404
11405   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11406 }
11407
11408 void GameActions_RND()
11409 {
11410   int magic_wall_x = 0, magic_wall_y = 0;
11411   int i, x, y, element, graphic;
11412
11413   InitPlayfieldScanModeVars();
11414
11415 #if USE_ONE_MORE_CHANGE_PER_FRAME
11416   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11417   {
11418     SCAN_PLAYFIELD(x, y)
11419     {
11420       ChangeCount[x][y] = 0;
11421       ChangeEvent[x][y] = -1;
11422     }
11423   }
11424 #endif
11425
11426   if (game.set_centered_player)
11427   {
11428     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11429
11430     /* switching to "all players" only possible if all players fit to screen */
11431     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11432     {
11433       game.centered_player_nr_next = game.centered_player_nr;
11434       game.set_centered_player = FALSE;
11435     }
11436
11437     /* do not switch focus to non-existing (or non-active) player */
11438     if (game.centered_player_nr_next >= 0 &&
11439         !stored_player[game.centered_player_nr_next].active)
11440     {
11441       game.centered_player_nr_next = game.centered_player_nr;
11442       game.set_centered_player = FALSE;
11443     }
11444   }
11445
11446   if (game.set_centered_player &&
11447       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11448   {
11449     int sx, sy;
11450
11451     if (game.centered_player_nr_next == -1)
11452     {
11453       setScreenCenteredToAllPlayers(&sx, &sy);
11454     }
11455     else
11456     {
11457       sx = stored_player[game.centered_player_nr_next].jx;
11458       sy = stored_player[game.centered_player_nr_next].jy;
11459     }
11460
11461     game.centered_player_nr = game.centered_player_nr_next;
11462     game.set_centered_player = FALSE;
11463
11464     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11465     DrawGameDoorValues();
11466   }
11467
11468   for (i = 0; i < MAX_PLAYERS; i++)
11469   {
11470     int actual_player_action = stored_player[i].effective_action;
11471
11472 #if 1
11473     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11474        - rnd_equinox_tetrachloride 048
11475        - rnd_equinox_tetrachloride_ii 096
11476        - rnd_emanuel_schmieg 002
11477        - doctor_sloan_ww 001, 020
11478     */
11479     if (stored_player[i].MovPos == 0)
11480       CheckGravityMovement(&stored_player[i]);
11481 #endif
11482
11483     /* overwrite programmed action with tape action */
11484     if (stored_player[i].programmed_action)
11485       actual_player_action = stored_player[i].programmed_action;
11486
11487     PlayerActions(&stored_player[i], actual_player_action);
11488
11489     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11490   }
11491
11492   ScrollScreen(NULL, SCROLL_GO_ON);
11493
11494   /* for backwards compatibility, the following code emulates a fixed bug that
11495      occured when pushing elements (causing elements that just made their last
11496      pushing step to already (if possible) make their first falling step in the
11497      same game frame, which is bad); this code is also needed to use the famous
11498      "spring push bug" which is used in older levels and might be wanted to be
11499      used also in newer levels, but in this case the buggy pushing code is only
11500      affecting the "spring" element and no other elements */
11501
11502   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11503   {
11504     for (i = 0; i < MAX_PLAYERS; i++)
11505     {
11506       struct PlayerInfo *player = &stored_player[i];
11507       int x = player->jx;
11508       int y = player->jy;
11509
11510       if (player->active && player->is_pushing && player->is_moving &&
11511           IS_MOVING(x, y) &&
11512           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11513            Feld[x][y] == EL_SPRING))
11514       {
11515         ContinueMoving(x, y);
11516
11517         /* continue moving after pushing (this is actually a bug) */
11518         if (!IS_MOVING(x, y))
11519           Stop[x][y] = FALSE;
11520       }
11521     }
11522   }
11523
11524 #if 0
11525   debug_print_timestamp(0, "start main loop profiling");
11526 #endif
11527
11528   SCAN_PLAYFIELD(x, y)
11529   {
11530     ChangeCount[x][y] = 0;
11531     ChangeEvent[x][y] = -1;
11532
11533     /* this must be handled before main playfield loop */
11534     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11535     {
11536       MovDelay[x][y]--;
11537       if (MovDelay[x][y] <= 0)
11538         RemoveField(x, y);
11539     }
11540
11541 #if USE_NEW_SNAP_DELAY
11542     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11543     {
11544       MovDelay[x][y]--;
11545       if (MovDelay[x][y] <= 0)
11546       {
11547         RemoveField(x, y);
11548         DrawLevelField(x, y);
11549
11550         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11551       }
11552     }
11553 #endif
11554
11555 #if DEBUG
11556     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11557     {
11558       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11559       printf("GameActions(): This should never happen!\n");
11560
11561       ChangePage[x][y] = -1;
11562     }
11563 #endif
11564
11565     Stop[x][y] = FALSE;
11566     if (WasJustMoving[x][y] > 0)
11567       WasJustMoving[x][y]--;
11568     if (WasJustFalling[x][y] > 0)
11569       WasJustFalling[x][y]--;
11570     if (CheckCollision[x][y] > 0)
11571       CheckCollision[x][y]--;
11572     if (CheckImpact[x][y] > 0)
11573       CheckImpact[x][y]--;
11574
11575     GfxFrame[x][y]++;
11576
11577     /* reset finished pushing action (not done in ContinueMoving() to allow
11578        continuous pushing animation for elements with zero push delay) */
11579     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11580     {
11581       ResetGfxAnimation(x, y);
11582       DrawLevelField(x, y);
11583     }
11584
11585 #if DEBUG
11586     if (IS_BLOCKED(x, y))
11587     {
11588       int oldx, oldy;
11589
11590       Blocked2Moving(x, y, &oldx, &oldy);
11591       if (!IS_MOVING(oldx, oldy))
11592       {
11593         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11594         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11595         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11596         printf("GameActions(): This should never happen!\n");
11597       }
11598     }
11599 #endif
11600   }
11601
11602 #if 0
11603   debug_print_timestamp(0, "- time for pre-main loop:");
11604 #endif
11605
11606 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11607   SCAN_PLAYFIELD(x, y)
11608   {
11609     element = Feld[x][y];
11610     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11611
11612 #if 1
11613     {
11614 #if 1
11615       int element2 = element;
11616       int graphic2 = graphic;
11617 #else
11618       int element2 = Feld[x][y];
11619       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11620 #endif
11621       int last_gfx_frame = GfxFrame[x][y];
11622
11623       if (graphic_info[graphic2].anim_global_sync)
11624         GfxFrame[x][y] = FrameCounter;
11625       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11626         GfxFrame[x][y] = CustomValue[x][y];
11627       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11628         GfxFrame[x][y] = element_info[element2].collect_score;
11629       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11630         GfxFrame[x][y] = ChangeDelay[x][y];
11631
11632       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11633         DrawLevelGraphicAnimation(x, y, graphic2);
11634     }
11635 #else
11636     ResetGfxFrame(x, y, TRUE);
11637 #endif
11638
11639 #if 1
11640     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11641         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11642       ResetRandomAnimationValue(x, y);
11643 #endif
11644
11645 #if 1
11646     SetRandomAnimationValue(x, y);
11647 #endif
11648
11649 #if 1
11650     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11651 #endif
11652   }
11653 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11654
11655 #if 0
11656   debug_print_timestamp(0, "- time for TEST loop:     -->");
11657 #endif
11658
11659   SCAN_PLAYFIELD(x, y)
11660   {
11661     element = Feld[x][y];
11662     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11663
11664     ResetGfxFrame(x, y, TRUE);
11665
11666     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11667         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11668       ResetRandomAnimationValue(x, y);
11669
11670     SetRandomAnimationValue(x, y);
11671
11672     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11673
11674     if (IS_INACTIVE(element))
11675     {
11676       if (IS_ANIMATED(graphic))
11677         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11678
11679       continue;
11680     }
11681
11682     /* this may take place after moving, so 'element' may have changed */
11683     if (IS_CHANGING(x, y) &&
11684         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11685     {
11686       int page = element_info[element].event_page_nr[CE_DELAY];
11687
11688 #if 1
11689       HandleElementChange(x, y, page);
11690 #else
11691       if (CAN_CHANGE(element))
11692         HandleElementChange(x, y, page);
11693
11694       if (HAS_ACTION(element))
11695         ExecuteCustomElementAction(x, y, element, page);
11696 #endif
11697
11698       element = Feld[x][y];
11699       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11700     }
11701
11702 #if 0   // ---------------------------------------------------------------------
11703
11704     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11705     {
11706       StartMoving(x, y);
11707
11708       element = Feld[x][y];
11709       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11710
11711       if (IS_ANIMATED(graphic) &&
11712           !IS_MOVING(x, y) &&
11713           !Stop[x][y])
11714         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11715
11716       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11717         DrawTwinkleOnField(x, y);
11718     }
11719     else if (IS_MOVING(x, y))
11720       ContinueMoving(x, y);
11721     else
11722     {
11723       switch (element)
11724       {
11725         case EL_ACID:
11726         case EL_EXIT_OPEN:
11727         case EL_EM_EXIT_OPEN:
11728         case EL_SP_EXIT_OPEN:
11729         case EL_STEEL_EXIT_OPEN:
11730         case EL_EM_STEEL_EXIT_OPEN:
11731         case EL_SP_TERMINAL:
11732         case EL_SP_TERMINAL_ACTIVE:
11733         case EL_EXTRA_TIME:
11734         case EL_SHIELD_NORMAL:
11735         case EL_SHIELD_DEADLY:
11736           if (IS_ANIMATED(graphic))
11737             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11738           break;
11739
11740         case EL_DYNAMITE_ACTIVE:
11741         case EL_EM_DYNAMITE_ACTIVE:
11742         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11743         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11744         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11745         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11746         case EL_SP_DISK_RED_ACTIVE:
11747           CheckDynamite(x, y);
11748           break;
11749
11750         case EL_AMOEBA_GROWING:
11751           AmoebeWaechst(x, y);
11752           break;
11753
11754         case EL_AMOEBA_SHRINKING:
11755           AmoebaDisappearing(x, y);
11756           break;
11757
11758 #if !USE_NEW_AMOEBA_CODE
11759         case EL_AMOEBA_WET:
11760         case EL_AMOEBA_DRY:
11761         case EL_AMOEBA_FULL:
11762         case EL_BD_AMOEBA:
11763         case EL_EMC_DRIPPER:
11764           AmoebeAbleger(x, y);
11765           break;
11766 #endif
11767
11768         case EL_GAME_OF_LIFE:
11769         case EL_BIOMAZE:
11770           Life(x, y);
11771           break;
11772
11773         case EL_EXIT_CLOSED:
11774           CheckExit(x, y);
11775           break;
11776
11777         case EL_EM_EXIT_CLOSED:
11778           CheckExitEM(x, y);
11779           break;
11780
11781         case EL_STEEL_EXIT_CLOSED:
11782           CheckExitSteel(x, y);
11783           break;
11784
11785         case EL_EM_STEEL_EXIT_CLOSED:
11786           CheckExitSteelEM(x, y);
11787           break;
11788
11789         case EL_SP_EXIT_CLOSED:
11790           CheckExitSP(x, y);
11791           break;
11792
11793         case EL_EXPANDABLE_WALL_GROWING:
11794         case EL_EXPANDABLE_STEELWALL_GROWING:
11795           MauerWaechst(x, y);
11796           break;
11797
11798         case EL_EXPANDABLE_WALL:
11799         case EL_EXPANDABLE_WALL_HORIZONTAL:
11800         case EL_EXPANDABLE_WALL_VERTICAL:
11801         case EL_EXPANDABLE_WALL_ANY:
11802         case EL_BD_EXPANDABLE_WALL:
11803           MauerAbleger(x, y);
11804           break;
11805
11806         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11807         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11808         case EL_EXPANDABLE_STEELWALL_ANY:
11809           MauerAblegerStahl(x, y);
11810           break;
11811
11812         case EL_FLAMES:
11813           CheckForDragon(x, y);
11814           break;
11815
11816         case EL_EXPLOSION:
11817           break;
11818
11819         case EL_ELEMENT_SNAPPING:
11820         case EL_DIAGONAL_SHRINKING:
11821         case EL_DIAGONAL_GROWING:
11822         {
11823           graphic =
11824             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11825
11826           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11827           break;
11828         }
11829
11830         default:
11831           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11832             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11833           break;
11834       }
11835     }
11836
11837 #else   // ---------------------------------------------------------------------
11838
11839     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11840     {
11841       StartMoving(x, y);
11842
11843       element = Feld[x][y];
11844       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11845
11846       if (IS_ANIMATED(graphic) &&
11847           !IS_MOVING(x, y) &&
11848           !Stop[x][y])
11849         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11850
11851       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11852         DrawTwinkleOnField(x, y);
11853     }
11854     else if ((element == EL_ACID ||
11855               element == EL_EXIT_OPEN ||
11856               element == EL_EM_EXIT_OPEN ||
11857               element == EL_SP_EXIT_OPEN ||
11858               element == EL_STEEL_EXIT_OPEN ||
11859               element == EL_EM_STEEL_EXIT_OPEN ||
11860               element == EL_SP_TERMINAL ||
11861               element == EL_SP_TERMINAL_ACTIVE ||
11862               element == EL_EXTRA_TIME ||
11863               element == EL_SHIELD_NORMAL ||
11864               element == EL_SHIELD_DEADLY) &&
11865              IS_ANIMATED(graphic))
11866       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11867     else if (IS_MOVING(x, y))
11868       ContinueMoving(x, y);
11869     else if (IS_ACTIVE_BOMB(element))
11870       CheckDynamite(x, y);
11871     else if (element == EL_AMOEBA_GROWING)
11872       AmoebeWaechst(x, y);
11873     else if (element == EL_AMOEBA_SHRINKING)
11874       AmoebaDisappearing(x, y);
11875
11876 #if !USE_NEW_AMOEBA_CODE
11877     else if (IS_AMOEBALIVE(element))
11878       AmoebeAbleger(x, y);
11879 #endif
11880
11881     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11882       Life(x, y);
11883     else if (element == EL_EXIT_CLOSED)
11884       CheckExit(x, y);
11885     else if (element == EL_EM_EXIT_CLOSED)
11886       CheckExitEM(x, y);
11887     else if (element == EL_STEEL_EXIT_CLOSED)
11888       CheckExitSteel(x, y);
11889     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11890       CheckExitSteelEM(x, y);
11891     else if (element == EL_SP_EXIT_CLOSED)
11892       CheckExitSP(x, y);
11893     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11894              element == EL_EXPANDABLE_STEELWALL_GROWING)
11895       MauerWaechst(x, y);
11896     else if (element == EL_EXPANDABLE_WALL ||
11897              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11898              element == EL_EXPANDABLE_WALL_VERTICAL ||
11899              element == EL_EXPANDABLE_WALL_ANY ||
11900              element == EL_BD_EXPANDABLE_WALL)
11901       MauerAbleger(x, y);
11902     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11903              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11904              element == EL_EXPANDABLE_STEELWALL_ANY)
11905       MauerAblegerStahl(x, y);
11906     else if (element == EL_FLAMES)
11907       CheckForDragon(x, y);
11908     else if (element == EL_EXPLOSION)
11909       ; /* drawing of correct explosion animation is handled separately */
11910     else if (element == EL_ELEMENT_SNAPPING ||
11911              element == EL_DIAGONAL_SHRINKING ||
11912              element == EL_DIAGONAL_GROWING)
11913     {
11914       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11915
11916       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11917     }
11918     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11919       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11920
11921 #endif  // ---------------------------------------------------------------------
11922
11923     if (IS_BELT_ACTIVE(element))
11924       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11925
11926     if (game.magic_wall_active)
11927     {
11928       int jx = local_player->jx, jy = local_player->jy;
11929
11930       /* play the element sound at the position nearest to the player */
11931       if ((element == EL_MAGIC_WALL_FULL ||
11932            element == EL_MAGIC_WALL_ACTIVE ||
11933            element == EL_MAGIC_WALL_EMPTYING ||
11934            element == EL_BD_MAGIC_WALL_FULL ||
11935            element == EL_BD_MAGIC_WALL_ACTIVE ||
11936            element == EL_BD_MAGIC_WALL_EMPTYING ||
11937            element == EL_DC_MAGIC_WALL_FULL ||
11938            element == EL_DC_MAGIC_WALL_ACTIVE ||
11939            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11940           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11941       {
11942         magic_wall_x = x;
11943         magic_wall_y = y;
11944       }
11945     }
11946   }
11947
11948 #if 0
11949   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11950 #endif
11951
11952 #if USE_NEW_AMOEBA_CODE
11953   /* new experimental amoeba growth stuff */
11954   if (!(FrameCounter % 8))
11955   {
11956     static unsigned long random = 1684108901;
11957
11958     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11959     {
11960       x = RND(lev_fieldx);
11961       y = RND(lev_fieldy);
11962       element = Feld[x][y];
11963
11964       if (!IS_PLAYER(x,y) &&
11965           (element == EL_EMPTY ||
11966            CAN_GROW_INTO(element) ||
11967            element == EL_QUICKSAND_EMPTY ||
11968            element == EL_QUICKSAND_FAST_EMPTY ||
11969            element == EL_ACID_SPLASH_LEFT ||
11970            element == EL_ACID_SPLASH_RIGHT))
11971       {
11972         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11973             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11974             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11975             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11976           Feld[x][y] = EL_AMOEBA_DROP;
11977       }
11978
11979       random = random * 129 + 1;
11980     }
11981   }
11982 #endif
11983
11984 #if 0
11985   if (game.explosions_delayed)
11986 #endif
11987   {
11988     game.explosions_delayed = FALSE;
11989
11990     SCAN_PLAYFIELD(x, y)
11991     {
11992       element = Feld[x][y];
11993
11994       if (ExplodeField[x][y])
11995         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11996       else if (element == EL_EXPLOSION)
11997         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11998
11999       ExplodeField[x][y] = EX_TYPE_NONE;
12000     }
12001
12002     game.explosions_delayed = TRUE;
12003   }
12004
12005   if (game.magic_wall_active)
12006   {
12007     if (!(game.magic_wall_time_left % 4))
12008     {
12009       int element = Feld[magic_wall_x][magic_wall_y];
12010
12011       if (element == EL_BD_MAGIC_WALL_FULL ||
12012           element == EL_BD_MAGIC_WALL_ACTIVE ||
12013           element == EL_BD_MAGIC_WALL_EMPTYING)
12014         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12015       else if (element == EL_DC_MAGIC_WALL_FULL ||
12016                element == EL_DC_MAGIC_WALL_ACTIVE ||
12017                element == EL_DC_MAGIC_WALL_EMPTYING)
12018         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12019       else
12020         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12021     }
12022
12023     if (game.magic_wall_time_left > 0)
12024     {
12025       game.magic_wall_time_left--;
12026
12027       if (!game.magic_wall_time_left)
12028       {
12029         SCAN_PLAYFIELD(x, y)
12030         {
12031           element = Feld[x][y];
12032
12033           if (element == EL_MAGIC_WALL_ACTIVE ||
12034               element == EL_MAGIC_WALL_FULL)
12035           {
12036             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12037             DrawLevelField(x, y);
12038           }
12039           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12040                    element == EL_BD_MAGIC_WALL_FULL)
12041           {
12042             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12043             DrawLevelField(x, y);
12044           }
12045           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12046                    element == EL_DC_MAGIC_WALL_FULL)
12047           {
12048             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12049             DrawLevelField(x, y);
12050           }
12051         }
12052
12053         game.magic_wall_active = FALSE;
12054       }
12055     }
12056   }
12057
12058   if (game.light_time_left > 0)
12059   {
12060     game.light_time_left--;
12061
12062     if (game.light_time_left == 0)
12063       RedrawAllLightSwitchesAndInvisibleElements();
12064   }
12065
12066   if (game.timegate_time_left > 0)
12067   {
12068     game.timegate_time_left--;
12069
12070     if (game.timegate_time_left == 0)
12071       CloseAllOpenTimegates();
12072   }
12073
12074   if (game.lenses_time_left > 0)
12075   {
12076     game.lenses_time_left--;
12077
12078     if (game.lenses_time_left == 0)
12079       RedrawAllInvisibleElementsForLenses();
12080   }
12081
12082   if (game.magnify_time_left > 0)
12083   {
12084     game.magnify_time_left--;
12085
12086     if (game.magnify_time_left == 0)
12087       RedrawAllInvisibleElementsForMagnifier();
12088   }
12089
12090   for (i = 0; i < MAX_PLAYERS; i++)
12091   {
12092     struct PlayerInfo *player = &stored_player[i];
12093
12094     if (SHIELD_ON(player))
12095     {
12096       if (player->shield_deadly_time_left)
12097         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12098       else if (player->shield_normal_time_left)
12099         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12100     }
12101   }
12102
12103   CheckLevelTime();
12104
12105   DrawAllPlayers();
12106   PlayAllPlayersSound();
12107
12108   if (options.debug)                    /* calculate frames per second */
12109   {
12110     static unsigned long fps_counter = 0;
12111     static int fps_frames = 0;
12112     unsigned long fps_delay_ms = Counter() - fps_counter;
12113
12114     fps_frames++;
12115
12116     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12117     {
12118       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12119
12120       fps_frames = 0;
12121       fps_counter = Counter();
12122     }
12123
12124     redraw_mask |= REDRAW_FPS;
12125   }
12126
12127   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12128
12129   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12130   {
12131     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12132
12133     local_player->show_envelope = 0;
12134   }
12135
12136 #if 0
12137   debug_print_timestamp(0, "stop main loop profiling ");
12138   printf("----------------------------------------------------------\n");
12139 #endif
12140
12141   /* use random number generator in every frame to make it less predictable */
12142   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12143     RND(1);
12144 }
12145
12146 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12147 {
12148   int min_x = x, min_y = y, max_x = x, max_y = y;
12149   int i;
12150
12151   for (i = 0; i < MAX_PLAYERS; i++)
12152   {
12153     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12154
12155     if (!stored_player[i].active || &stored_player[i] == player)
12156       continue;
12157
12158     min_x = MIN(min_x, jx);
12159     min_y = MIN(min_y, jy);
12160     max_x = MAX(max_x, jx);
12161     max_y = MAX(max_y, jy);
12162   }
12163
12164   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12165 }
12166
12167 static boolean AllPlayersInVisibleScreen()
12168 {
12169   int i;
12170
12171   for (i = 0; i < MAX_PLAYERS; i++)
12172   {
12173     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12174
12175     if (!stored_player[i].active)
12176       continue;
12177
12178     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12179       return FALSE;
12180   }
12181
12182   return TRUE;
12183 }
12184
12185 void ScrollLevel(int dx, int dy)
12186 {
12187 #if 1
12188   static Bitmap *bitmap_db_field2 = NULL;
12189   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12190   int x, y;
12191 #else
12192   int i, x, y;
12193 #endif
12194
12195 #if 0
12196   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12197   /* only horizontal XOR vertical scroll direction allowed */
12198   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12199     return;
12200 #endif
12201
12202 #if 1
12203   if (bitmap_db_field2 == NULL)
12204     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12205
12206   /* needed when blitting directly to same bitmap -- should not be needed with
12207      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12208   BlitBitmap(drawto_field, bitmap_db_field2,
12209              FX + TILEX * (dx == -1) - softscroll_offset,
12210              FY + TILEY * (dy == -1) - softscroll_offset,
12211              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12212              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12213              FX + TILEX * (dx == 1) - softscroll_offset,
12214              FY + TILEY * (dy == 1) - softscroll_offset);
12215   BlitBitmap(bitmap_db_field2, drawto_field,
12216              FX + TILEX * (dx == 1) - softscroll_offset,
12217              FY + TILEY * (dy == 1) - softscroll_offset,
12218              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12219              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12220              FX + TILEX * (dx == 1) - softscroll_offset,
12221              FY + TILEY * (dy == 1) - softscroll_offset);
12222
12223 #else
12224
12225 #if 1
12226   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12227   int xsize = (BX2 - BX1 + 1);
12228   int ysize = (BY2 - BY1 + 1);
12229   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12230   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12231   int step  = (start < end ? +1 : -1);
12232
12233   for (i = start; i != end; i += step)
12234   {
12235     BlitBitmap(drawto_field, drawto_field,
12236                FX + TILEX * (dx != 0 ? i + step : 0),
12237                FY + TILEY * (dy != 0 ? i + step : 0),
12238                TILEX * (dx != 0 ? 1 : xsize),
12239                TILEY * (dy != 0 ? 1 : ysize),
12240                FX + TILEX * (dx != 0 ? i : 0),
12241                FY + TILEY * (dy != 0 ? i : 0));
12242   }
12243
12244 #else
12245
12246   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12247
12248   BlitBitmap(drawto_field, drawto_field,
12249              FX + TILEX * (dx == -1) - softscroll_offset,
12250              FY + TILEY * (dy == -1) - softscroll_offset,
12251              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12252              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12253              FX + TILEX * (dx == 1) - softscroll_offset,
12254              FY + TILEY * (dy == 1) - softscroll_offset);
12255 #endif
12256 #endif
12257
12258   if (dx != 0)
12259   {
12260     x = (dx == 1 ? BX1 : BX2);
12261     for (y = BY1; y <= BY2; y++)
12262       DrawScreenField(x, y);
12263   }
12264
12265   if (dy != 0)
12266   {
12267     y = (dy == 1 ? BY1 : BY2);
12268     for (x = BX1; x <= BX2; x++)
12269       DrawScreenField(x, y);
12270   }
12271
12272   redraw_mask |= REDRAW_FIELD;
12273 }
12274
12275 static boolean canFallDown(struct PlayerInfo *player)
12276 {
12277   int jx = player->jx, jy = player->jy;
12278
12279   return (IN_LEV_FIELD(jx, jy + 1) &&
12280           (IS_FREE(jx, jy + 1) ||
12281            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12282           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12283           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12284 }
12285
12286 static boolean canPassField(int x, int y, int move_dir)
12287 {
12288   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12289   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12290   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12291   int nextx = x + dx;
12292   int nexty = y + dy;
12293   int element = Feld[x][y];
12294
12295   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12296           !CAN_MOVE(element) &&
12297           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12298           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12299           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12300 }
12301
12302 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12303 {
12304   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12305   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12306   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12307   int newx = x + dx;
12308   int newy = y + dy;
12309
12310   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12311           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12312           (IS_DIGGABLE(Feld[newx][newy]) ||
12313            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12314            canPassField(newx, newy, move_dir)));
12315 }
12316
12317 static void CheckGravityMovement(struct PlayerInfo *player)
12318 {
12319 #if USE_PLAYER_GRAVITY
12320   if (player->gravity && !player->programmed_action)
12321 #else
12322   if (game.gravity && !player->programmed_action)
12323 #endif
12324   {
12325     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12326     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12327     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12328     int jx = player->jx, jy = player->jy;
12329     boolean player_is_moving_to_valid_field =
12330       (!player_is_snapping &&
12331        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12332         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12333     boolean player_can_fall_down = canFallDown(player);
12334
12335     if (player_can_fall_down &&
12336         !player_is_moving_to_valid_field)
12337       player->programmed_action = MV_DOWN;
12338   }
12339 }
12340
12341 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12342 {
12343   return CheckGravityMovement(player);
12344
12345 #if USE_PLAYER_GRAVITY
12346   if (player->gravity && !player->programmed_action)
12347 #else
12348   if (game.gravity && !player->programmed_action)
12349 #endif
12350   {
12351     int jx = player->jx, jy = player->jy;
12352     boolean field_under_player_is_free =
12353       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12354     boolean player_is_standing_on_valid_field =
12355       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12356        (IS_WALKABLE(Feld[jx][jy]) &&
12357         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12358
12359     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12360       player->programmed_action = MV_DOWN;
12361   }
12362 }
12363
12364 /*
12365   MovePlayerOneStep()
12366   -----------------------------------------------------------------------------
12367   dx, dy:               direction (non-diagonal) to try to move the player to
12368   real_dx, real_dy:     direction as read from input device (can be diagonal)
12369 */
12370
12371 boolean MovePlayerOneStep(struct PlayerInfo *player,
12372                           int dx, int dy, int real_dx, int real_dy)
12373 {
12374   int jx = player->jx, jy = player->jy;
12375   int new_jx = jx + dx, new_jy = jy + dy;
12376 #if !USE_FIXED_DONT_RUN_INTO
12377   int element;
12378 #endif
12379   int can_move;
12380   boolean player_can_move = !player->cannot_move;
12381
12382   if (!player->active || (!dx && !dy))
12383     return MP_NO_ACTION;
12384
12385   player->MovDir = (dx < 0 ? MV_LEFT :
12386                     dx > 0 ? MV_RIGHT :
12387                     dy < 0 ? MV_UP :
12388                     dy > 0 ? MV_DOWN :  MV_NONE);
12389
12390   if (!IN_LEV_FIELD(new_jx, new_jy))
12391     return MP_NO_ACTION;
12392
12393   if (!player_can_move)
12394   {
12395     if (player->MovPos == 0)
12396     {
12397       player->is_moving = FALSE;
12398       player->is_digging = FALSE;
12399       player->is_collecting = FALSE;
12400       player->is_snapping = FALSE;
12401       player->is_pushing = FALSE;
12402     }
12403   }
12404
12405 #if 1
12406   if (!options.network && game.centered_player_nr == -1 &&
12407       !AllPlayersInSight(player, new_jx, new_jy))
12408     return MP_NO_ACTION;
12409 #else
12410   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12411     return MP_NO_ACTION;
12412 #endif
12413
12414 #if !USE_FIXED_DONT_RUN_INTO
12415   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12416
12417   /* (moved to DigField()) */
12418   if (player_can_move && DONT_RUN_INTO(element))
12419   {
12420     if (element == EL_ACID && dx == 0 && dy == 1)
12421     {
12422       SplashAcid(new_jx, new_jy);
12423       Feld[jx][jy] = EL_PLAYER_1;
12424       InitMovingField(jx, jy, MV_DOWN);
12425       Store[jx][jy] = EL_ACID;
12426       ContinueMoving(jx, jy);
12427       BuryPlayer(player);
12428     }
12429     else
12430       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12431
12432     return MP_MOVING;
12433   }
12434 #endif
12435
12436   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12437   if (can_move != MP_MOVING)
12438     return can_move;
12439
12440   /* check if DigField() has caused relocation of the player */
12441   if (player->jx != jx || player->jy != jy)
12442     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12443
12444   StorePlayer[jx][jy] = 0;
12445   player->last_jx = jx;
12446   player->last_jy = jy;
12447   player->jx = new_jx;
12448   player->jy = new_jy;
12449   StorePlayer[new_jx][new_jy] = player->element_nr;
12450
12451   if (player->move_delay_value_next != -1)
12452   {
12453     player->move_delay_value = player->move_delay_value_next;
12454     player->move_delay_value_next = -1;
12455   }
12456
12457   player->MovPos =
12458     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12459
12460   player->step_counter++;
12461
12462   PlayerVisit[jx][jy] = FrameCounter;
12463
12464 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12465   player->is_moving = TRUE;
12466 #endif
12467
12468 #if 1
12469   /* should better be called in MovePlayer(), but this breaks some tapes */
12470   ScrollPlayer(player, SCROLL_INIT);
12471 #endif
12472
12473   return MP_MOVING;
12474 }
12475
12476 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12477 {
12478   int jx = player->jx, jy = player->jy;
12479   int old_jx = jx, old_jy = jy;
12480   int moved = MP_NO_ACTION;
12481
12482   if (!player->active)
12483     return FALSE;
12484
12485   if (!dx && !dy)
12486   {
12487     if (player->MovPos == 0)
12488     {
12489       player->is_moving = FALSE;
12490       player->is_digging = FALSE;
12491       player->is_collecting = FALSE;
12492       player->is_snapping = FALSE;
12493       player->is_pushing = FALSE;
12494     }
12495
12496     return FALSE;
12497   }
12498
12499   if (player->move_delay > 0)
12500     return FALSE;
12501
12502   player->move_delay = -1;              /* set to "uninitialized" value */
12503
12504   /* store if player is automatically moved to next field */
12505   player->is_auto_moving = (player->programmed_action != MV_NONE);
12506
12507   /* remove the last programmed player action */
12508   player->programmed_action = 0;
12509
12510   if (player->MovPos)
12511   {
12512     /* should only happen if pre-1.2 tape recordings are played */
12513     /* this is only for backward compatibility */
12514
12515     int original_move_delay_value = player->move_delay_value;
12516
12517 #if DEBUG
12518     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12519            tape.counter);
12520 #endif
12521
12522     /* scroll remaining steps with finest movement resolution */
12523     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12524
12525     while (player->MovPos)
12526     {
12527       ScrollPlayer(player, SCROLL_GO_ON);
12528       ScrollScreen(NULL, SCROLL_GO_ON);
12529
12530       AdvanceFrameAndPlayerCounters(player->index_nr);
12531
12532       DrawAllPlayers();
12533       BackToFront();
12534     }
12535
12536     player->move_delay_value = original_move_delay_value;
12537   }
12538
12539   player->is_active = FALSE;
12540
12541   if (player->last_move_dir & MV_HORIZONTAL)
12542   {
12543     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12544       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12545   }
12546   else
12547   {
12548     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12549       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12550   }
12551
12552 #if USE_FIXED_BORDER_RUNNING_GFX
12553   if (!moved && !player->is_active)
12554   {
12555     player->is_moving = FALSE;
12556     player->is_digging = FALSE;
12557     player->is_collecting = FALSE;
12558     player->is_snapping = FALSE;
12559     player->is_pushing = FALSE;
12560   }
12561 #endif
12562
12563   jx = player->jx;
12564   jy = player->jy;
12565
12566 #if 1
12567   if (moved & MP_MOVING && !ScreenMovPos &&
12568       (player->index_nr == game.centered_player_nr ||
12569        game.centered_player_nr == -1))
12570 #else
12571   if (moved & MP_MOVING && !ScreenMovPos &&
12572       (player == local_player || !options.network))
12573 #endif
12574   {
12575     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12576     int offset = game.scroll_delay_value;
12577
12578     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12579     {
12580       /* actual player has left the screen -- scroll in that direction */
12581       if (jx != old_jx)         /* player has moved horizontally */
12582         scroll_x += (jx - old_jx);
12583       else                      /* player has moved vertically */
12584         scroll_y += (jy - old_jy);
12585     }
12586     else
12587     {
12588       if (jx != old_jx)         /* player has moved horizontally */
12589       {
12590         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12591             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12592           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12593
12594         /* don't scroll over playfield boundaries */
12595         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12596           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12597
12598         /* don't scroll more than one field at a time */
12599         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12600
12601         /* don't scroll against the player's moving direction */
12602         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12603             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12604           scroll_x = old_scroll_x;
12605       }
12606       else                      /* player has moved vertically */
12607       {
12608         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12609             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12610           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12611
12612         /* don't scroll over playfield boundaries */
12613         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12614           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12615
12616         /* don't scroll more than one field at a time */
12617         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12618
12619         /* don't scroll against the player's moving direction */
12620         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12621             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12622           scroll_y = old_scroll_y;
12623       }
12624     }
12625
12626     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12627     {
12628 #if 1
12629       if (!options.network && game.centered_player_nr == -1 &&
12630           !AllPlayersInVisibleScreen())
12631       {
12632         scroll_x = old_scroll_x;
12633         scroll_y = old_scroll_y;
12634       }
12635       else
12636 #else
12637       if (!options.network && !AllPlayersInVisibleScreen())
12638       {
12639         scroll_x = old_scroll_x;
12640         scroll_y = old_scroll_y;
12641       }
12642       else
12643 #endif
12644       {
12645         ScrollScreen(player, SCROLL_INIT);
12646         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12647       }
12648     }
12649   }
12650
12651   player->StepFrame = 0;
12652
12653   if (moved & MP_MOVING)
12654   {
12655     if (old_jx != jx && old_jy == jy)
12656       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12657     else if (old_jx == jx && old_jy != jy)
12658       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12659
12660     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12661
12662     player->last_move_dir = player->MovDir;
12663     player->is_moving = TRUE;
12664     player->is_snapping = FALSE;
12665     player->is_switching = FALSE;
12666     player->is_dropping = FALSE;
12667     player->is_dropping_pressed = FALSE;
12668     player->drop_pressed_delay = 0;
12669
12670 #if 0
12671     /* should better be called here than above, but this breaks some tapes */
12672     ScrollPlayer(player, SCROLL_INIT);
12673 #endif
12674   }
12675   else
12676   {
12677     CheckGravityMovementWhenNotMoving(player);
12678
12679     player->is_moving = FALSE;
12680
12681     /* at this point, the player is allowed to move, but cannot move right now
12682        (e.g. because of something blocking the way) -- ensure that the player
12683        is also allowed to move in the next frame (in old versions before 3.1.1,
12684        the player was forced to wait again for eight frames before next try) */
12685
12686     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12687       player->move_delay = 0;   /* allow direct movement in the next frame */
12688   }
12689
12690   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12691     player->move_delay = player->move_delay_value;
12692
12693   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12694   {
12695     TestIfPlayerTouchesBadThing(jx, jy);
12696     TestIfPlayerTouchesCustomElement(jx, jy);
12697   }
12698
12699   if (!player->active)
12700     RemovePlayer(player);
12701
12702   return moved;
12703 }
12704
12705 void ScrollPlayer(struct PlayerInfo *player, int mode)
12706 {
12707   int jx = player->jx, jy = player->jy;
12708   int last_jx = player->last_jx, last_jy = player->last_jy;
12709   int move_stepsize = TILEX / player->move_delay_value;
12710
12711 #if USE_NEW_PLAYER_SPEED
12712   if (!player->active)
12713     return;
12714
12715   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12716     return;
12717 #else
12718   if (!player->active || player->MovPos == 0)
12719     return;
12720 #endif
12721
12722   if (mode == SCROLL_INIT)
12723   {
12724     player->actual_frame_counter = FrameCounter;
12725     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12726
12727     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12728         Feld[last_jx][last_jy] == EL_EMPTY)
12729     {
12730       int last_field_block_delay = 0;   /* start with no blocking at all */
12731       int block_delay_adjustment = player->block_delay_adjustment;
12732
12733       /* if player blocks last field, add delay for exactly one move */
12734       if (player->block_last_field)
12735       {
12736         last_field_block_delay += player->move_delay_value;
12737
12738         /* when blocking enabled, prevent moving up despite gravity */
12739 #if USE_PLAYER_GRAVITY
12740         if (player->gravity && player->MovDir == MV_UP)
12741           block_delay_adjustment = -1;
12742 #else
12743         if (game.gravity && player->MovDir == MV_UP)
12744           block_delay_adjustment = -1;
12745 #endif
12746       }
12747
12748       /* add block delay adjustment (also possible when not blocking) */
12749       last_field_block_delay += block_delay_adjustment;
12750
12751       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12752       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12753     }
12754
12755 #if USE_NEW_PLAYER_SPEED
12756     if (player->MovPos != 0)    /* player has not yet reached destination */
12757       return;
12758 #else
12759     return;
12760 #endif
12761   }
12762   else if (!FrameReached(&player->actual_frame_counter, 1))
12763     return;
12764
12765 #if USE_NEW_PLAYER_SPEED
12766   if (player->MovPos != 0)
12767   {
12768     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12769     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12770
12771     /* before DrawPlayer() to draw correct player graphic for this case */
12772     if (player->MovPos == 0)
12773       CheckGravityMovement(player);
12774   }
12775 #else
12776   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12777   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12778
12779   /* before DrawPlayer() to draw correct player graphic for this case */
12780   if (player->MovPos == 0)
12781     CheckGravityMovement(player);
12782 #endif
12783
12784   if (player->MovPos == 0)      /* player reached destination field */
12785   {
12786     if (player->move_delay_reset_counter > 0)
12787     {
12788       player->move_delay_reset_counter--;
12789
12790       if (player->move_delay_reset_counter == 0)
12791       {
12792         /* continue with normal speed after quickly moving through gate */
12793         HALVE_PLAYER_SPEED(player);
12794
12795         /* be able to make the next move without delay */
12796         player->move_delay = 0;
12797       }
12798     }
12799
12800     player->last_jx = jx;
12801     player->last_jy = jy;
12802
12803     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12804         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12805         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12806         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12807         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12808         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12809     {
12810       DrawPlayer(player);       /* needed here only to cleanup last field */
12811       RemovePlayer(player);
12812
12813       if (local_player->friends_still_needed == 0 ||
12814           IS_SP_ELEMENT(Feld[jx][jy]))
12815         PlayerWins(player);
12816     }
12817
12818     /* this breaks one level: "machine", level 000 */
12819     {
12820       int move_direction = player->MovDir;
12821       int enter_side = MV_DIR_OPPOSITE(move_direction);
12822       int leave_side = move_direction;
12823       int old_jx = last_jx;
12824       int old_jy = last_jy;
12825       int old_element = Feld[old_jx][old_jy];
12826       int new_element = Feld[jx][jy];
12827
12828       if (IS_CUSTOM_ELEMENT(old_element))
12829         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12830                                    CE_LEFT_BY_PLAYER,
12831                                    player->index_bit, leave_side);
12832
12833       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12834                                           CE_PLAYER_LEAVES_X,
12835                                           player->index_bit, leave_side);
12836
12837       if (IS_CUSTOM_ELEMENT(new_element))
12838         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12839                                    player->index_bit, enter_side);
12840
12841       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12842                                           CE_PLAYER_ENTERS_X,
12843                                           player->index_bit, enter_side);
12844
12845       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12846                                         CE_MOVE_OF_X, move_direction);
12847     }
12848
12849     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12850     {
12851       TestIfPlayerTouchesBadThing(jx, jy);
12852       TestIfPlayerTouchesCustomElement(jx, jy);
12853
12854       /* needed because pushed element has not yet reached its destination,
12855          so it would trigger a change event at its previous field location */
12856       if (!player->is_pushing)
12857         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12858
12859       if (!player->active)
12860         RemovePlayer(player);
12861     }
12862
12863     if (!local_player->LevelSolved && level.use_step_counter)
12864     {
12865       int i;
12866
12867       TimePlayed++;
12868
12869       if (TimeLeft > 0)
12870       {
12871         TimeLeft--;
12872
12873         if (TimeLeft <= 10 && setup.time_limit)
12874           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12875
12876 #if 1
12877         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12878
12879         DisplayGameControlValues();
12880 #else
12881         DrawGameValue_Time(TimeLeft);
12882 #endif
12883
12884         if (!TimeLeft && setup.time_limit)
12885           for (i = 0; i < MAX_PLAYERS; i++)
12886             KillPlayer(&stored_player[i]);
12887       }
12888 #if 1
12889       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12890       {
12891         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12892
12893         DisplayGameControlValues();
12894       }
12895 #else
12896       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12897         DrawGameValue_Time(TimePlayed);
12898 #endif
12899     }
12900
12901     if (tape.single_step && tape.recording && !tape.pausing &&
12902         !player->programmed_action)
12903       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12904   }
12905 }
12906
12907 void ScrollScreen(struct PlayerInfo *player, int mode)
12908 {
12909   static unsigned long screen_frame_counter = 0;
12910
12911   if (mode == SCROLL_INIT)
12912   {
12913     /* set scrolling step size according to actual player's moving speed */
12914     ScrollStepSize = TILEX / player->move_delay_value;
12915
12916     screen_frame_counter = FrameCounter;
12917     ScreenMovDir = player->MovDir;
12918     ScreenMovPos = player->MovPos;
12919     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12920     return;
12921   }
12922   else if (!FrameReached(&screen_frame_counter, 1))
12923     return;
12924
12925   if (ScreenMovPos)
12926   {
12927     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12928     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12929     redraw_mask |= REDRAW_FIELD;
12930   }
12931   else
12932     ScreenMovDir = MV_NONE;
12933 }
12934
12935 void TestIfPlayerTouchesCustomElement(int x, int y)
12936 {
12937   static int xy[4][2] =
12938   {
12939     { 0, -1 },
12940     { -1, 0 },
12941     { +1, 0 },
12942     { 0, +1 }
12943   };
12944   static int trigger_sides[4][2] =
12945   {
12946     /* center side       border side */
12947     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12948     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12949     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12950     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12951   };
12952   static int touch_dir[4] =
12953   {
12954     MV_LEFT | MV_RIGHT,
12955     MV_UP   | MV_DOWN,
12956     MV_UP   | MV_DOWN,
12957     MV_LEFT | MV_RIGHT
12958   };
12959   int center_element = Feld[x][y];      /* should always be non-moving! */
12960   int i;
12961
12962   for (i = 0; i < NUM_DIRECTIONS; i++)
12963   {
12964     int xx = x + xy[i][0];
12965     int yy = y + xy[i][1];
12966     int center_side = trigger_sides[i][0];
12967     int border_side = trigger_sides[i][1];
12968     int border_element;
12969
12970     if (!IN_LEV_FIELD(xx, yy))
12971       continue;
12972
12973     if (IS_PLAYER(x, y))
12974     {
12975       struct PlayerInfo *player = PLAYERINFO(x, y);
12976
12977       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12978         border_element = Feld[xx][yy];          /* may be moving! */
12979       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12980         border_element = Feld[xx][yy];
12981       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12982         border_element = MovingOrBlocked2Element(xx, yy);
12983       else
12984         continue;               /* center and border element do not touch */
12985
12986       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12987                                  player->index_bit, border_side);
12988       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12989                                           CE_PLAYER_TOUCHES_X,
12990                                           player->index_bit, border_side);
12991     }
12992     else if (IS_PLAYER(xx, yy))
12993     {
12994       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12995
12996       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12997       {
12998         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12999           continue;             /* center and border element do not touch */
13000       }
13001
13002       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13003                                  player->index_bit, center_side);
13004       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13005                                           CE_PLAYER_TOUCHES_X,
13006                                           player->index_bit, center_side);
13007       break;
13008     }
13009   }
13010 }
13011
13012 #if USE_ELEMENT_TOUCHING_BUGFIX
13013
13014 void TestIfElementTouchesCustomElement(int x, int y)
13015 {
13016   static int xy[4][2] =
13017   {
13018     { 0, -1 },
13019     { -1, 0 },
13020     { +1, 0 },
13021     { 0, +1 }
13022   };
13023   static int trigger_sides[4][2] =
13024   {
13025     /* center side      border side */
13026     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13027     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13028     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13029     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13030   };
13031   static int touch_dir[4] =
13032   {
13033     MV_LEFT | MV_RIGHT,
13034     MV_UP   | MV_DOWN,
13035     MV_UP   | MV_DOWN,
13036     MV_LEFT | MV_RIGHT
13037   };
13038   boolean change_center_element = FALSE;
13039   int center_element = Feld[x][y];      /* should always be non-moving! */
13040   int border_element_old[NUM_DIRECTIONS];
13041   int i;
13042
13043   for (i = 0; i < NUM_DIRECTIONS; i++)
13044   {
13045     int xx = x + xy[i][0];
13046     int yy = y + xy[i][1];
13047     int border_element;
13048
13049     border_element_old[i] = -1;
13050
13051     if (!IN_LEV_FIELD(xx, yy))
13052       continue;
13053
13054     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13055       border_element = Feld[xx][yy];    /* may be moving! */
13056     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13057       border_element = Feld[xx][yy];
13058     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13059       border_element = MovingOrBlocked2Element(xx, yy);
13060     else
13061       continue;                 /* center and border element do not touch */
13062
13063     border_element_old[i] = border_element;
13064   }
13065
13066   for (i = 0; i < NUM_DIRECTIONS; i++)
13067   {
13068     int xx = x + xy[i][0];
13069     int yy = y + xy[i][1];
13070     int center_side = trigger_sides[i][0];
13071     int border_element = border_element_old[i];
13072
13073     if (border_element == -1)
13074       continue;
13075
13076     /* check for change of border element */
13077     CheckElementChangeBySide(xx, yy, border_element, center_element,
13078                              CE_TOUCHING_X, center_side);
13079   }
13080
13081   for (i = 0; i < NUM_DIRECTIONS; i++)
13082   {
13083     int border_side = trigger_sides[i][1];
13084     int border_element = border_element_old[i];
13085
13086     if (border_element == -1)
13087       continue;
13088
13089     /* check for change of center element (but change it only once) */
13090     if (!change_center_element)
13091       change_center_element =
13092         CheckElementChangeBySide(x, y, center_element, border_element,
13093                                  CE_TOUCHING_X, border_side);
13094   }
13095 }
13096
13097 #else
13098
13099 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13100 {
13101   static int xy[4][2] =
13102   {
13103     { 0, -1 },
13104     { -1, 0 },
13105     { +1, 0 },
13106     { 0, +1 }
13107   };
13108   static int trigger_sides[4][2] =
13109   {
13110     /* center side      border side */
13111     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13112     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13113     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13114     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13115   };
13116   static int touch_dir[4] =
13117   {
13118     MV_LEFT | MV_RIGHT,
13119     MV_UP   | MV_DOWN,
13120     MV_UP   | MV_DOWN,
13121     MV_LEFT | MV_RIGHT
13122   };
13123   boolean change_center_element = FALSE;
13124   int center_element = Feld[x][y];      /* should always be non-moving! */
13125   int i;
13126
13127   for (i = 0; i < NUM_DIRECTIONS; i++)
13128   {
13129     int xx = x + xy[i][0];
13130     int yy = y + xy[i][1];
13131     int center_side = trigger_sides[i][0];
13132     int border_side = trigger_sides[i][1];
13133     int border_element;
13134
13135     if (!IN_LEV_FIELD(xx, yy))
13136       continue;
13137
13138     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13139       border_element = Feld[xx][yy];    /* may be moving! */
13140     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13141       border_element = Feld[xx][yy];
13142     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13143       border_element = MovingOrBlocked2Element(xx, yy);
13144     else
13145       continue;                 /* center and border element do not touch */
13146
13147     /* check for change of center element (but change it only once) */
13148     if (!change_center_element)
13149       change_center_element =
13150         CheckElementChangeBySide(x, y, center_element, border_element,
13151                                  CE_TOUCHING_X, border_side);
13152
13153     /* check for change of border element */
13154     CheckElementChangeBySide(xx, yy, border_element, center_element,
13155                              CE_TOUCHING_X, center_side);
13156   }
13157 }
13158
13159 #endif
13160
13161 void TestIfElementHitsCustomElement(int x, int y, int direction)
13162 {
13163   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13164   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13165   int hitx = x + dx, hity = y + dy;
13166   int hitting_element = Feld[x][y];
13167   int touched_element;
13168
13169   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13170     return;
13171
13172   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13173                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13174
13175   if (IN_LEV_FIELD(hitx, hity))
13176   {
13177     int opposite_direction = MV_DIR_OPPOSITE(direction);
13178     int hitting_side = direction;
13179     int touched_side = opposite_direction;
13180     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13181                           MovDir[hitx][hity] != direction ||
13182                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13183
13184     object_hit = TRUE;
13185
13186     if (object_hit)
13187     {
13188       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13189                                CE_HITTING_X, touched_side);
13190
13191       CheckElementChangeBySide(hitx, hity, touched_element,
13192                                hitting_element, CE_HIT_BY_X, hitting_side);
13193
13194       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13195                                CE_HIT_BY_SOMETHING, opposite_direction);
13196     }
13197   }
13198
13199   /* "hitting something" is also true when hitting the playfield border */
13200   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13201                            CE_HITTING_SOMETHING, direction);
13202 }
13203
13204 #if 0
13205 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13206 {
13207   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13208   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13209   int hitx = x + dx, hity = y + dy;
13210   int hitting_element = Feld[x][y];
13211   int touched_element;
13212 #if 0
13213   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13214                         !IS_FREE(hitx, hity) &&
13215                         (!IS_MOVING(hitx, hity) ||
13216                          MovDir[hitx][hity] != direction ||
13217                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13218 #endif
13219
13220   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13221     return;
13222
13223 #if 0
13224   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13225     return;
13226 #endif
13227
13228   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13229                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13230
13231   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13232                            EP_CAN_SMASH_EVERYTHING, direction);
13233
13234   if (IN_LEV_FIELD(hitx, hity))
13235   {
13236     int opposite_direction = MV_DIR_OPPOSITE(direction);
13237     int hitting_side = direction;
13238     int touched_side = opposite_direction;
13239 #if 0
13240     int touched_element = MovingOrBlocked2Element(hitx, hity);
13241 #endif
13242 #if 1
13243     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13244                           MovDir[hitx][hity] != direction ||
13245                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13246
13247     object_hit = TRUE;
13248 #endif
13249
13250     if (object_hit)
13251     {
13252       int i;
13253
13254       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13255                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13256
13257       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13258                                CE_OTHER_IS_SMASHING, touched_side);
13259
13260       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13261                                CE_OTHER_GETS_SMASHED, hitting_side);
13262     }
13263   }
13264 }
13265 #endif
13266
13267 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13268 {
13269   int i, kill_x = -1, kill_y = -1;
13270
13271   int bad_element = -1;
13272   static int test_xy[4][2] =
13273   {
13274     { 0, -1 },
13275     { -1, 0 },
13276     { +1, 0 },
13277     { 0, +1 }
13278   };
13279   static int test_dir[4] =
13280   {
13281     MV_UP,
13282     MV_LEFT,
13283     MV_RIGHT,
13284     MV_DOWN
13285   };
13286
13287   for (i = 0; i < NUM_DIRECTIONS; i++)
13288   {
13289     int test_x, test_y, test_move_dir, test_element;
13290
13291     test_x = good_x + test_xy[i][0];
13292     test_y = good_y + test_xy[i][1];
13293
13294     if (!IN_LEV_FIELD(test_x, test_y))
13295       continue;
13296
13297     test_move_dir =
13298       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13299
13300     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13301
13302     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13303        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13304     */
13305     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13306         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13307     {
13308       kill_x = test_x;
13309       kill_y = test_y;
13310       bad_element = test_element;
13311
13312       break;
13313     }
13314   }
13315
13316   if (kill_x != -1 || kill_y != -1)
13317   {
13318     if (IS_PLAYER(good_x, good_y))
13319     {
13320       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13321
13322       if (player->shield_deadly_time_left > 0 &&
13323           !IS_INDESTRUCTIBLE(bad_element))
13324         Bang(kill_x, kill_y);
13325       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13326         KillPlayer(player);
13327     }
13328     else
13329       Bang(good_x, good_y);
13330   }
13331 }
13332
13333 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13334 {
13335   int i, kill_x = -1, kill_y = -1;
13336   int bad_element = Feld[bad_x][bad_y];
13337   static int test_xy[4][2] =
13338   {
13339     { 0, -1 },
13340     { -1, 0 },
13341     { +1, 0 },
13342     { 0, +1 }
13343   };
13344   static int touch_dir[4] =
13345   {
13346     MV_LEFT | MV_RIGHT,
13347     MV_UP   | MV_DOWN,
13348     MV_UP   | MV_DOWN,
13349     MV_LEFT | MV_RIGHT
13350   };
13351   static int test_dir[4] =
13352   {
13353     MV_UP,
13354     MV_LEFT,
13355     MV_RIGHT,
13356     MV_DOWN
13357   };
13358
13359   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13360     return;
13361
13362   for (i = 0; i < NUM_DIRECTIONS; i++)
13363   {
13364     int test_x, test_y, test_move_dir, test_element;
13365
13366     test_x = bad_x + test_xy[i][0];
13367     test_y = bad_y + test_xy[i][1];
13368     if (!IN_LEV_FIELD(test_x, test_y))
13369       continue;
13370
13371     test_move_dir =
13372       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13373
13374     test_element = Feld[test_x][test_y];
13375
13376     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13377        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13378     */
13379     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13380         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13381     {
13382       /* good thing is player or penguin that does not move away */
13383       if (IS_PLAYER(test_x, test_y))
13384       {
13385         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13386
13387         if (bad_element == EL_ROBOT && player->is_moving)
13388           continue;     /* robot does not kill player if he is moving */
13389
13390         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13391         {
13392           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13393             continue;           /* center and border element do not touch */
13394         }
13395
13396         kill_x = test_x;
13397         kill_y = test_y;
13398         break;
13399       }
13400       else if (test_element == EL_PENGUIN)
13401       {
13402         kill_x = test_x;
13403         kill_y = test_y;
13404         break;
13405       }
13406     }
13407   }
13408
13409   if (kill_x != -1 || kill_y != -1)
13410   {
13411     if (IS_PLAYER(kill_x, kill_y))
13412     {
13413       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13414
13415       if (player->shield_deadly_time_left > 0 &&
13416           !IS_INDESTRUCTIBLE(bad_element))
13417         Bang(bad_x, bad_y);
13418       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13419         KillPlayer(player);
13420     }
13421     else
13422       Bang(kill_x, kill_y);
13423   }
13424 }
13425
13426 void TestIfPlayerTouchesBadThing(int x, int y)
13427 {
13428   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13429 }
13430
13431 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13432 {
13433   TestIfGoodThingHitsBadThing(x, y, move_dir);
13434 }
13435
13436 void TestIfBadThingTouchesPlayer(int x, int y)
13437 {
13438   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13439 }
13440
13441 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13442 {
13443   TestIfBadThingHitsGoodThing(x, y, move_dir);
13444 }
13445
13446 void TestIfFriendTouchesBadThing(int x, int y)
13447 {
13448   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13449 }
13450
13451 void TestIfBadThingTouchesFriend(int x, int y)
13452 {
13453   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13454 }
13455
13456 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13457 {
13458   int i, kill_x = bad_x, kill_y = bad_y;
13459   static int xy[4][2] =
13460   {
13461     { 0, -1 },
13462     { -1, 0 },
13463     { +1, 0 },
13464     { 0, +1 }
13465   };
13466
13467   for (i = 0; i < NUM_DIRECTIONS; i++)
13468   {
13469     int x, y, element;
13470
13471     x = bad_x + xy[i][0];
13472     y = bad_y + xy[i][1];
13473     if (!IN_LEV_FIELD(x, y))
13474       continue;
13475
13476     element = Feld[x][y];
13477     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13478         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13479     {
13480       kill_x = x;
13481       kill_y = y;
13482       break;
13483     }
13484   }
13485
13486   if (kill_x != bad_x || kill_y != bad_y)
13487     Bang(bad_x, bad_y);
13488 }
13489
13490 void KillPlayer(struct PlayerInfo *player)
13491 {
13492   int jx = player->jx, jy = player->jy;
13493
13494   if (!player->active)
13495     return;
13496
13497   /* the following code was introduced to prevent an infinite loop when calling
13498      -> Bang()
13499      -> CheckTriggeredElementChangeExt()
13500      -> ExecuteCustomElementAction()
13501      -> KillPlayer()
13502      -> (infinitely repeating the above sequence of function calls)
13503      which occurs when killing the player while having a CE with the setting
13504      "kill player X when explosion of <player X>"; the solution using a new
13505      field "player->killed" was chosen for backwards compatibility, although
13506      clever use of the fields "player->active" etc. would probably also work */
13507 #if 1
13508   if (player->killed)
13509     return;
13510 #endif
13511
13512   player->killed = TRUE;
13513
13514   /* remove accessible field at the player's position */
13515   Feld[jx][jy] = EL_EMPTY;
13516
13517   /* deactivate shield (else Bang()/Explode() would not work right) */
13518   player->shield_normal_time_left = 0;
13519   player->shield_deadly_time_left = 0;
13520
13521   Bang(jx, jy);
13522   BuryPlayer(player);
13523 }
13524
13525 static void KillPlayerUnlessEnemyProtected(int x, int y)
13526 {
13527   if (!PLAYER_ENEMY_PROTECTED(x, y))
13528     KillPlayer(PLAYERINFO(x, y));
13529 }
13530
13531 static void KillPlayerUnlessExplosionProtected(int x, int y)
13532 {
13533   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13534     KillPlayer(PLAYERINFO(x, y));
13535 }
13536
13537 void BuryPlayer(struct PlayerInfo *player)
13538 {
13539   int jx = player->jx, jy = player->jy;
13540
13541   if (!player->active)
13542     return;
13543
13544   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13545   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13546
13547   player->GameOver = TRUE;
13548   RemovePlayer(player);
13549 }
13550
13551 void RemovePlayer(struct PlayerInfo *player)
13552 {
13553   int jx = player->jx, jy = player->jy;
13554   int i, found = FALSE;
13555
13556   player->present = FALSE;
13557   player->active = FALSE;
13558
13559   if (!ExplodeField[jx][jy])
13560     StorePlayer[jx][jy] = 0;
13561
13562   if (player->is_moving)
13563     DrawLevelField(player->last_jx, player->last_jy);
13564
13565   for (i = 0; i < MAX_PLAYERS; i++)
13566     if (stored_player[i].active)
13567       found = TRUE;
13568
13569   if (!found)
13570     AllPlayersGone = TRUE;
13571
13572   ExitX = ZX = jx;
13573   ExitY = ZY = jy;
13574 }
13575
13576 #if USE_NEW_SNAP_DELAY
13577 static void setFieldForSnapping(int x, int y, int element, int direction)
13578 {
13579   struct ElementInfo *ei = &element_info[element];
13580   int direction_bit = MV_DIR_TO_BIT(direction);
13581   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13582   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13583                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13584
13585   Feld[x][y] = EL_ELEMENT_SNAPPING;
13586   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13587
13588   ResetGfxAnimation(x, y);
13589
13590   GfxElement[x][y] = element;
13591   GfxAction[x][y] = action;
13592   GfxDir[x][y] = direction;
13593   GfxFrame[x][y] = -1;
13594 }
13595 #endif
13596
13597 /*
13598   =============================================================================
13599   checkDiagonalPushing()
13600   -----------------------------------------------------------------------------
13601   check if diagonal input device direction results in pushing of object
13602   (by checking if the alternative direction is walkable, diggable, ...)
13603   =============================================================================
13604 */
13605
13606 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13607                                     int x, int y, int real_dx, int real_dy)
13608 {
13609   int jx, jy, dx, dy, xx, yy;
13610
13611   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13612     return TRUE;
13613
13614   /* diagonal direction: check alternative direction */
13615   jx = player->jx;
13616   jy = player->jy;
13617   dx = x - jx;
13618   dy = y - jy;
13619   xx = jx + (dx == 0 ? real_dx : 0);
13620   yy = jy + (dy == 0 ? real_dy : 0);
13621
13622   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13623 }
13624
13625 /*
13626   =============================================================================
13627   DigField()
13628   -----------------------------------------------------------------------------
13629   x, y:                 field next to player (non-diagonal) to try to dig to
13630   real_dx, real_dy:     direction as read from input device (can be diagonal)
13631   =============================================================================
13632 */
13633
13634 int DigField(struct PlayerInfo *player,
13635              int oldx, int oldy, int x, int y,
13636              int real_dx, int real_dy, int mode)
13637 {
13638   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13639   boolean player_was_pushing = player->is_pushing;
13640   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13641   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13642   int jx = oldx, jy = oldy;
13643   int dx = x - jx, dy = y - jy;
13644   int nextx = x + dx, nexty = y + dy;
13645   int move_direction = (dx == -1 ? MV_LEFT  :
13646                         dx == +1 ? MV_RIGHT :
13647                         dy == -1 ? MV_UP    :
13648                         dy == +1 ? MV_DOWN  : MV_NONE);
13649   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13650   int dig_side = MV_DIR_OPPOSITE(move_direction);
13651   int old_element = Feld[jx][jy];
13652 #if USE_FIXED_DONT_RUN_INTO
13653   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13654 #else
13655   int element;
13656 #endif
13657   int collect_count;
13658
13659   if (is_player)                /* function can also be called by EL_PENGUIN */
13660   {
13661     if (player->MovPos == 0)
13662     {
13663       player->is_digging = FALSE;
13664       player->is_collecting = FALSE;
13665     }
13666
13667     if (player->MovPos == 0)    /* last pushing move finished */
13668       player->is_pushing = FALSE;
13669
13670     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13671     {
13672       player->is_switching = FALSE;
13673       player->push_delay = -1;
13674
13675       return MP_NO_ACTION;
13676     }
13677   }
13678
13679 #if !USE_FIXED_DONT_RUN_INTO
13680   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13681     return MP_NO_ACTION;
13682 #endif
13683
13684   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13685     old_element = Back[jx][jy];
13686
13687   /* in case of element dropped at player position, check background */
13688   else if (Back[jx][jy] != EL_EMPTY &&
13689            game.engine_version >= VERSION_IDENT(2,2,0,0))
13690     old_element = Back[jx][jy];
13691
13692   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13693     return MP_NO_ACTION;        /* field has no opening in this direction */
13694
13695   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13696     return MP_NO_ACTION;        /* field has no opening in this direction */
13697
13698 #if USE_FIXED_DONT_RUN_INTO
13699   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13700   {
13701     SplashAcid(x, y);
13702
13703     Feld[jx][jy] = player->artwork_element;
13704     InitMovingField(jx, jy, MV_DOWN);
13705     Store[jx][jy] = EL_ACID;
13706     ContinueMoving(jx, jy);
13707     BuryPlayer(player);
13708
13709     return MP_DONT_RUN_INTO;
13710   }
13711 #endif
13712
13713 #if USE_FIXED_DONT_RUN_INTO
13714   if (player_can_move && DONT_RUN_INTO(element))
13715   {
13716     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13717
13718     return MP_DONT_RUN_INTO;
13719   }
13720 #endif
13721
13722 #if USE_FIXED_DONT_RUN_INTO
13723   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13724     return MP_NO_ACTION;
13725 #endif
13726
13727 #if !USE_FIXED_DONT_RUN_INTO
13728   element = Feld[x][y];
13729 #endif
13730
13731   collect_count = element_info[element].collect_count_initial;
13732
13733   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13734     return MP_NO_ACTION;
13735
13736   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13737     player_can_move = player_can_move_or_snap;
13738
13739   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13740       game.engine_version >= VERSION_IDENT(2,2,0,0))
13741   {
13742     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13743                                player->index_bit, dig_side);
13744     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13745                                         player->index_bit, dig_side);
13746
13747     if (element == EL_DC_LANDMINE)
13748       Bang(x, y);
13749
13750     if (Feld[x][y] != element)          /* field changed by snapping */
13751       return MP_ACTION;
13752
13753     return MP_NO_ACTION;
13754   }
13755
13756 #if USE_PLAYER_GRAVITY
13757   if (player->gravity && is_player && !player->is_auto_moving &&
13758       canFallDown(player) && move_direction != MV_DOWN &&
13759       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13760     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13761 #else
13762   if (game.gravity && is_player && !player->is_auto_moving &&
13763       canFallDown(player) && move_direction != MV_DOWN &&
13764       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13765     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13766 #endif
13767
13768   if (player_can_move &&
13769       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13770   {
13771     int sound_element = SND_ELEMENT(element);
13772     int sound_action = ACTION_WALKING;
13773
13774     if (IS_RND_GATE(element))
13775     {
13776       if (!player->key[RND_GATE_NR(element)])
13777         return MP_NO_ACTION;
13778     }
13779     else if (IS_RND_GATE_GRAY(element))
13780     {
13781       if (!player->key[RND_GATE_GRAY_NR(element)])
13782         return MP_NO_ACTION;
13783     }
13784     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13785     {
13786       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13787         return MP_NO_ACTION;
13788     }
13789     else if (element == EL_EXIT_OPEN ||
13790              element == EL_EM_EXIT_OPEN ||
13791              element == EL_STEEL_EXIT_OPEN ||
13792              element == EL_EM_STEEL_EXIT_OPEN ||
13793              element == EL_SP_EXIT_OPEN ||
13794              element == EL_SP_EXIT_OPENING)
13795     {
13796       sound_action = ACTION_PASSING;    /* player is passing exit */
13797     }
13798     else if (element == EL_EMPTY)
13799     {
13800       sound_action = ACTION_MOVING;             /* nothing to walk on */
13801     }
13802
13803     /* play sound from background or player, whatever is available */
13804     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13805       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13806     else
13807       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13808   }
13809   else if (player_can_move &&
13810            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13811   {
13812     if (!ACCESS_FROM(element, opposite_direction))
13813       return MP_NO_ACTION;      /* field not accessible from this direction */
13814
13815     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13816       return MP_NO_ACTION;
13817
13818     if (IS_EM_GATE(element))
13819     {
13820       if (!player->key[EM_GATE_NR(element)])
13821         return MP_NO_ACTION;
13822     }
13823     else if (IS_EM_GATE_GRAY(element))
13824     {
13825       if (!player->key[EM_GATE_GRAY_NR(element)])
13826         return MP_NO_ACTION;
13827     }
13828     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13829     {
13830       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13831         return MP_NO_ACTION;
13832     }
13833     else if (IS_EMC_GATE(element))
13834     {
13835       if (!player->key[EMC_GATE_NR(element)])
13836         return MP_NO_ACTION;
13837     }
13838     else if (IS_EMC_GATE_GRAY(element))
13839     {
13840       if (!player->key[EMC_GATE_GRAY_NR(element)])
13841         return MP_NO_ACTION;
13842     }
13843     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13844     {
13845       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13846         return MP_NO_ACTION;
13847     }
13848     else if (element == EL_DC_GATE_WHITE ||
13849              element == EL_DC_GATE_WHITE_GRAY ||
13850              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13851     {
13852       if (player->num_white_keys == 0)
13853         return MP_NO_ACTION;
13854
13855       player->num_white_keys--;
13856     }
13857     else if (IS_SP_PORT(element))
13858     {
13859       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13860           element == EL_SP_GRAVITY_PORT_RIGHT ||
13861           element == EL_SP_GRAVITY_PORT_UP ||
13862           element == EL_SP_GRAVITY_PORT_DOWN)
13863 #if USE_PLAYER_GRAVITY
13864         player->gravity = !player->gravity;
13865 #else
13866         game.gravity = !game.gravity;
13867 #endif
13868       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13869                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13870                element == EL_SP_GRAVITY_ON_PORT_UP ||
13871                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13872 #if USE_PLAYER_GRAVITY
13873         player->gravity = TRUE;
13874 #else
13875         game.gravity = TRUE;
13876 #endif
13877       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13878                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13879                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13880                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13881 #if USE_PLAYER_GRAVITY
13882         player->gravity = FALSE;
13883 #else
13884         game.gravity = FALSE;
13885 #endif
13886     }
13887
13888     /* automatically move to the next field with double speed */
13889     player->programmed_action = move_direction;
13890
13891     if (player->move_delay_reset_counter == 0)
13892     {
13893       player->move_delay_reset_counter = 2;     /* two double speed steps */
13894
13895       DOUBLE_PLAYER_SPEED(player);
13896     }
13897
13898     PlayLevelSoundAction(x, y, ACTION_PASSING);
13899   }
13900   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13901   {
13902     RemoveField(x, y);
13903
13904     if (mode != DF_SNAP)
13905     {
13906       GfxElement[x][y] = GFX_ELEMENT(element);
13907       player->is_digging = TRUE;
13908     }
13909
13910     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13911
13912     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13913                                         player->index_bit, dig_side);
13914
13915     if (mode == DF_SNAP)
13916     {
13917 #if USE_NEW_SNAP_DELAY
13918       if (level.block_snap_field)
13919         setFieldForSnapping(x, y, element, move_direction);
13920       else
13921         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13922 #else
13923       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13924 #endif
13925
13926       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13927                                           player->index_bit, dig_side);
13928     }
13929   }
13930   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13931   {
13932     RemoveField(x, y);
13933
13934     if (is_player && mode != DF_SNAP)
13935     {
13936       GfxElement[x][y] = element;
13937       player->is_collecting = TRUE;
13938     }
13939
13940     if (element == EL_SPEED_PILL)
13941     {
13942       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13943     }
13944     else if (element == EL_EXTRA_TIME && level.time > 0)
13945     {
13946       TimeLeft += level.extra_time;
13947
13948 #if 1
13949       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13950
13951       DisplayGameControlValues();
13952 #else
13953       DrawGameValue_Time(TimeLeft);
13954 #endif
13955     }
13956     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13957     {
13958       player->shield_normal_time_left += level.shield_normal_time;
13959       if (element == EL_SHIELD_DEADLY)
13960         player->shield_deadly_time_left += level.shield_deadly_time;
13961     }
13962     else if (element == EL_DYNAMITE ||
13963              element == EL_EM_DYNAMITE ||
13964              element == EL_SP_DISK_RED)
13965     {
13966       if (player->inventory_size < MAX_INVENTORY_SIZE)
13967         player->inventory_element[player->inventory_size++] = element;
13968
13969       DrawGameDoorValues();
13970     }
13971     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13972     {
13973       player->dynabomb_count++;
13974       player->dynabombs_left++;
13975     }
13976     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13977     {
13978       player->dynabomb_size++;
13979     }
13980     else if (element == EL_DYNABOMB_INCREASE_POWER)
13981     {
13982       player->dynabomb_xl = TRUE;
13983     }
13984     else if (IS_KEY(element))
13985     {
13986       player->key[KEY_NR(element)] = TRUE;
13987
13988       DrawGameDoorValues();
13989     }
13990     else if (element == EL_DC_KEY_WHITE)
13991     {
13992       player->num_white_keys++;
13993
13994       /* display white keys? */
13995       /* DrawGameDoorValues(); */
13996     }
13997     else if (IS_ENVELOPE(element))
13998     {
13999       player->show_envelope = element;
14000     }
14001     else if (element == EL_EMC_LENSES)
14002     {
14003       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14004
14005       RedrawAllInvisibleElementsForLenses();
14006     }
14007     else if (element == EL_EMC_MAGNIFIER)
14008     {
14009       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14010
14011       RedrawAllInvisibleElementsForMagnifier();
14012     }
14013     else if (IS_DROPPABLE(element) ||
14014              IS_THROWABLE(element))     /* can be collected and dropped */
14015     {
14016       int i;
14017
14018       if (collect_count == 0)
14019         player->inventory_infinite_element = element;
14020       else
14021         for (i = 0; i < collect_count; i++)
14022           if (player->inventory_size < MAX_INVENTORY_SIZE)
14023             player->inventory_element[player->inventory_size++] = element;
14024
14025       DrawGameDoorValues();
14026     }
14027     else if (collect_count > 0)
14028     {
14029       local_player->gems_still_needed -= collect_count;
14030       if (local_player->gems_still_needed < 0)
14031         local_player->gems_still_needed = 0;
14032
14033 #if 1
14034       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14035
14036       DisplayGameControlValues();
14037 #else
14038       DrawGameValue_Emeralds(local_player->gems_still_needed);
14039 #endif
14040     }
14041
14042     RaiseScoreElement(element);
14043     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14044
14045     if (is_player)
14046       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14047                                           player->index_bit, dig_side);
14048
14049     if (mode == DF_SNAP)
14050     {
14051 #if USE_NEW_SNAP_DELAY
14052       if (level.block_snap_field)
14053         setFieldForSnapping(x, y, element, move_direction);
14054       else
14055         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14056 #else
14057       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14058 #endif
14059
14060       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14061                                           player->index_bit, dig_side);
14062     }
14063   }
14064   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14065   {
14066     if (mode == DF_SNAP && element != EL_BD_ROCK)
14067       return MP_NO_ACTION;
14068
14069     if (CAN_FALL(element) && dy)
14070       return MP_NO_ACTION;
14071
14072     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14073         !(element == EL_SPRING && level.use_spring_bug))
14074       return MP_NO_ACTION;
14075
14076     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14077         ((move_direction & MV_VERTICAL &&
14078           ((element_info[element].move_pattern & MV_LEFT &&
14079             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14080            (element_info[element].move_pattern & MV_RIGHT &&
14081             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14082          (move_direction & MV_HORIZONTAL &&
14083           ((element_info[element].move_pattern & MV_UP &&
14084             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14085            (element_info[element].move_pattern & MV_DOWN &&
14086             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14087       return MP_NO_ACTION;
14088
14089     /* do not push elements already moving away faster than player */
14090     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14091         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14092       return MP_NO_ACTION;
14093
14094     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14095     {
14096       if (player->push_delay_value == -1 || !player_was_pushing)
14097         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14098     }
14099     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14100     {
14101       if (player->push_delay_value == -1)
14102         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14103     }
14104     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14105     {
14106       if (!player->is_pushing)
14107         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14108     }
14109
14110     player->is_pushing = TRUE;
14111     player->is_active = TRUE;
14112
14113     if (!(IN_LEV_FIELD(nextx, nexty) &&
14114           (IS_FREE(nextx, nexty) ||
14115            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14116             IS_SB_ELEMENT(element)))))
14117       return MP_NO_ACTION;
14118
14119     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14120       return MP_NO_ACTION;
14121
14122     if (player->push_delay == -1)       /* new pushing; restart delay */
14123       player->push_delay = 0;
14124
14125     if (player->push_delay < player->push_delay_value &&
14126         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14127         element != EL_SPRING && element != EL_BALLOON)
14128     {
14129       /* make sure that there is no move delay before next try to push */
14130       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14131         player->move_delay = 0;
14132
14133       return MP_NO_ACTION;
14134     }
14135
14136     if (IS_SB_ELEMENT(element))
14137     {
14138       if (element == EL_SOKOBAN_FIELD_FULL)
14139       {
14140         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14141         local_player->sokobanfields_still_needed++;
14142       }
14143
14144       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14145       {
14146         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14147         local_player->sokobanfields_still_needed--;
14148       }
14149
14150       Feld[x][y] = EL_SOKOBAN_OBJECT;
14151
14152       if (Back[x][y] == Back[nextx][nexty])
14153         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14154       else if (Back[x][y] != 0)
14155         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14156                                     ACTION_EMPTYING);
14157       else
14158         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14159                                     ACTION_FILLING);
14160
14161       if (local_player->sokobanfields_still_needed == 0 &&
14162           game.emulation == EMU_SOKOBAN)
14163       {
14164         PlayerWins(player);
14165
14166         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14167       }
14168     }
14169     else
14170       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14171
14172     InitMovingField(x, y, move_direction);
14173     GfxAction[x][y] = ACTION_PUSHING;
14174
14175     if (mode == DF_SNAP)
14176       ContinueMoving(x, y);
14177     else
14178       MovPos[x][y] = (dx != 0 ? dx : dy);
14179
14180     Pushed[x][y] = TRUE;
14181     Pushed[nextx][nexty] = TRUE;
14182
14183     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14184       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14185     else
14186       player->push_delay_value = -1;    /* get new value later */
14187
14188     /* check for element change _after_ element has been pushed */
14189     if (game.use_change_when_pushing_bug)
14190     {
14191       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14192                                  player->index_bit, dig_side);
14193       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14194                                           player->index_bit, dig_side);
14195     }
14196   }
14197   else if (IS_SWITCHABLE(element))
14198   {
14199     if (PLAYER_SWITCHING(player, x, y))
14200     {
14201       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14202                                           player->index_bit, dig_side);
14203
14204       return MP_ACTION;
14205     }
14206
14207     player->is_switching = TRUE;
14208     player->switch_x = x;
14209     player->switch_y = y;
14210
14211     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14212
14213     if (element == EL_ROBOT_WHEEL)
14214     {
14215       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14216       ZX = x;
14217       ZY = y;
14218
14219       game.robot_wheel_active = TRUE;
14220
14221       DrawLevelField(x, y);
14222     }
14223     else if (element == EL_SP_TERMINAL)
14224     {
14225       int xx, yy;
14226
14227       SCAN_PLAYFIELD(xx, yy)
14228       {
14229         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14230           Bang(xx, yy);
14231         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14232           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14233       }
14234     }
14235     else if (IS_BELT_SWITCH(element))
14236     {
14237       ToggleBeltSwitch(x, y);
14238     }
14239     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14240              element == EL_SWITCHGATE_SWITCH_DOWN ||
14241              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14242              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14243     {
14244       ToggleSwitchgateSwitch(x, y);
14245     }
14246     else if (element == EL_LIGHT_SWITCH ||
14247              element == EL_LIGHT_SWITCH_ACTIVE)
14248     {
14249       ToggleLightSwitch(x, y);
14250     }
14251     else if (element == EL_TIMEGATE_SWITCH ||
14252              element == EL_DC_TIMEGATE_SWITCH)
14253     {
14254       ActivateTimegateSwitch(x, y);
14255     }
14256     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14257              element == EL_BALLOON_SWITCH_RIGHT ||
14258              element == EL_BALLOON_SWITCH_UP    ||
14259              element == EL_BALLOON_SWITCH_DOWN  ||
14260              element == EL_BALLOON_SWITCH_NONE  ||
14261              element == EL_BALLOON_SWITCH_ANY)
14262     {
14263       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14264                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14265                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14266                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14267                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14268                              move_direction);
14269     }
14270     else if (element == EL_LAMP)
14271     {
14272       Feld[x][y] = EL_LAMP_ACTIVE;
14273       local_player->lights_still_needed--;
14274
14275       ResetGfxAnimation(x, y);
14276       DrawLevelField(x, y);
14277     }
14278     else if (element == EL_TIME_ORB_FULL)
14279     {
14280       Feld[x][y] = EL_TIME_ORB_EMPTY;
14281
14282       if (level.time > 0 || level.use_time_orb_bug)
14283       {
14284         TimeLeft += level.time_orb_time;
14285
14286 #if 1
14287         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14288
14289         DisplayGameControlValues();
14290 #else
14291         DrawGameValue_Time(TimeLeft);
14292 #endif
14293       }
14294
14295       ResetGfxAnimation(x, y);
14296       DrawLevelField(x, y);
14297     }
14298     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14299              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14300     {
14301       int xx, yy;
14302
14303       game.ball_state = !game.ball_state;
14304
14305       SCAN_PLAYFIELD(xx, yy)
14306       {
14307         int e = Feld[xx][yy];
14308
14309         if (game.ball_state)
14310         {
14311           if (e == EL_EMC_MAGIC_BALL)
14312             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14313           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14314             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14315         }
14316         else
14317         {
14318           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14319             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14320           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14321             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14322         }
14323       }
14324     }
14325
14326     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14327                                         player->index_bit, dig_side);
14328
14329     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14330                                         player->index_bit, dig_side);
14331
14332     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14333                                         player->index_bit, dig_side);
14334
14335     return MP_ACTION;
14336   }
14337   else
14338   {
14339     if (!PLAYER_SWITCHING(player, x, y))
14340     {
14341       player->is_switching = TRUE;
14342       player->switch_x = x;
14343       player->switch_y = y;
14344
14345       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14346                                  player->index_bit, dig_side);
14347       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14348                                           player->index_bit, dig_side);
14349
14350       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14351                                  player->index_bit, dig_side);
14352       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14353                                           player->index_bit, dig_side);
14354     }
14355
14356     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14357                                player->index_bit, dig_side);
14358     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14359                                         player->index_bit, dig_side);
14360
14361     return MP_NO_ACTION;
14362   }
14363
14364   player->push_delay = -1;
14365
14366   if (is_player)                /* function can also be called by EL_PENGUIN */
14367   {
14368     if (Feld[x][y] != element)          /* really digged/collected something */
14369     {
14370       player->is_collecting = !player->is_digging;
14371       player->is_active = TRUE;
14372     }
14373   }
14374
14375   return MP_MOVING;
14376 }
14377
14378 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14379 {
14380   int jx = player->jx, jy = player->jy;
14381   int x = jx + dx, y = jy + dy;
14382   int snap_direction = (dx == -1 ? MV_LEFT  :
14383                         dx == +1 ? MV_RIGHT :
14384                         dy == -1 ? MV_UP    :
14385                         dy == +1 ? MV_DOWN  : MV_NONE);
14386   boolean can_continue_snapping = (level.continuous_snapping &&
14387                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14388
14389   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14390     return FALSE;
14391
14392   if (!player->active || !IN_LEV_FIELD(x, y))
14393     return FALSE;
14394
14395   if (dx && dy)
14396     return FALSE;
14397
14398   if (!dx && !dy)
14399   {
14400     if (player->MovPos == 0)
14401       player->is_pushing = FALSE;
14402
14403     player->is_snapping = FALSE;
14404
14405     if (player->MovPos == 0)
14406     {
14407       player->is_moving = FALSE;
14408       player->is_digging = FALSE;
14409       player->is_collecting = FALSE;
14410     }
14411
14412     return FALSE;
14413   }
14414
14415 #if USE_NEW_CONTINUOUS_SNAPPING
14416   /* prevent snapping with already pressed snap key when not allowed */
14417   if (player->is_snapping && !can_continue_snapping)
14418     return FALSE;
14419 #else
14420   if (player->is_snapping)
14421     return FALSE;
14422 #endif
14423
14424   player->MovDir = snap_direction;
14425
14426   if (player->MovPos == 0)
14427   {
14428     player->is_moving = FALSE;
14429     player->is_digging = FALSE;
14430     player->is_collecting = FALSE;
14431   }
14432
14433   player->is_dropping = FALSE;
14434   player->is_dropping_pressed = FALSE;
14435   player->drop_pressed_delay = 0;
14436
14437   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14438     return FALSE;
14439
14440   player->is_snapping = TRUE;
14441   player->is_active = TRUE;
14442
14443   if (player->MovPos == 0)
14444   {
14445     player->is_moving = FALSE;
14446     player->is_digging = FALSE;
14447     player->is_collecting = FALSE;
14448   }
14449
14450   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14451     DrawLevelField(player->last_jx, player->last_jy);
14452
14453   DrawLevelField(x, y);
14454
14455   return TRUE;
14456 }
14457
14458 boolean DropElement(struct PlayerInfo *player)
14459 {
14460   int old_element, new_element;
14461   int dropx = player->jx, dropy = player->jy;
14462   int drop_direction = player->MovDir;
14463   int drop_side = drop_direction;
14464 #if 1
14465   int drop_element = get_next_dropped_element(player);
14466 #else
14467   int drop_element = (player->inventory_size > 0 ?
14468                       player->inventory_element[player->inventory_size - 1] :
14469                       player->inventory_infinite_element != EL_UNDEFINED ?
14470                       player->inventory_infinite_element :
14471                       player->dynabombs_left > 0 ?
14472                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14473                       EL_UNDEFINED);
14474 #endif
14475
14476   player->is_dropping_pressed = TRUE;
14477
14478   /* do not drop an element on top of another element; when holding drop key
14479      pressed without moving, dropped element must move away before the next
14480      element can be dropped (this is especially important if the next element
14481      is dynamite, which can be placed on background for historical reasons) */
14482   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14483     return MP_ACTION;
14484
14485   if (IS_THROWABLE(drop_element))
14486   {
14487     dropx += GET_DX_FROM_DIR(drop_direction);
14488     dropy += GET_DY_FROM_DIR(drop_direction);
14489
14490     if (!IN_LEV_FIELD(dropx, dropy))
14491       return FALSE;
14492   }
14493
14494   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14495   new_element = drop_element;           /* default: no change when dropping */
14496
14497   /* check if player is active, not moving and ready to drop */
14498   if (!player->active || player->MovPos || player->drop_delay > 0)
14499     return FALSE;
14500
14501   /* check if player has anything that can be dropped */
14502   if (new_element == EL_UNDEFINED)
14503     return FALSE;
14504
14505   /* check if drop key was pressed long enough for EM style dynamite */
14506   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14507     return FALSE;
14508
14509   /* check if anything can be dropped at the current position */
14510   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14511     return FALSE;
14512
14513   /* collected custom elements can only be dropped on empty fields */
14514   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14515     return FALSE;
14516
14517   if (old_element != EL_EMPTY)
14518     Back[dropx][dropy] = old_element;   /* store old element on this field */
14519
14520   ResetGfxAnimation(dropx, dropy);
14521   ResetRandomAnimationValue(dropx, dropy);
14522
14523   if (player->inventory_size > 0 ||
14524       player->inventory_infinite_element != EL_UNDEFINED)
14525   {
14526     if (player->inventory_size > 0)
14527     {
14528       player->inventory_size--;
14529
14530       DrawGameDoorValues();
14531
14532       if (new_element == EL_DYNAMITE)
14533         new_element = EL_DYNAMITE_ACTIVE;
14534       else if (new_element == EL_EM_DYNAMITE)
14535         new_element = EL_EM_DYNAMITE_ACTIVE;
14536       else if (new_element == EL_SP_DISK_RED)
14537         new_element = EL_SP_DISK_RED_ACTIVE;
14538     }
14539
14540     Feld[dropx][dropy] = new_element;
14541
14542     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14543       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14544                           el2img(Feld[dropx][dropy]), 0);
14545
14546     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14547
14548     /* needed if previous element just changed to "empty" in the last frame */
14549     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14550
14551     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14552                                player->index_bit, drop_side);
14553     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14554                                         CE_PLAYER_DROPS_X,
14555                                         player->index_bit, drop_side);
14556
14557     TestIfElementTouchesCustomElement(dropx, dropy);
14558   }
14559   else          /* player is dropping a dyna bomb */
14560   {
14561     player->dynabombs_left--;
14562
14563     Feld[dropx][dropy] = new_element;
14564
14565     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14566       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14567                           el2img(Feld[dropx][dropy]), 0);
14568
14569     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14570   }
14571
14572   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14573     InitField_WithBug1(dropx, dropy, FALSE);
14574
14575   new_element = Feld[dropx][dropy];     /* element might have changed */
14576
14577   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14578       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14579   {
14580     int move_direction, nextx, nexty;
14581
14582     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14583       MovDir[dropx][dropy] = drop_direction;
14584
14585     move_direction = MovDir[dropx][dropy];
14586     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14587     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14588
14589     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14590
14591 #if USE_FIX_IMPACT_COLLISION
14592     /* do not cause impact style collision by dropping elements that can fall */
14593     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14594 #else
14595     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14596 #endif
14597   }
14598
14599   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14600   player->is_dropping = TRUE;
14601
14602   player->drop_pressed_delay = 0;
14603   player->is_dropping_pressed = FALSE;
14604
14605   player->drop_x = dropx;
14606   player->drop_y = dropy;
14607
14608   return TRUE;
14609 }
14610
14611 /* ------------------------------------------------------------------------- */
14612 /* game sound playing functions                                              */
14613 /* ------------------------------------------------------------------------- */
14614
14615 static int *loop_sound_frame = NULL;
14616 static int *loop_sound_volume = NULL;
14617
14618 void InitPlayLevelSound()
14619 {
14620   int num_sounds = getSoundListSize();
14621
14622   checked_free(loop_sound_frame);
14623   checked_free(loop_sound_volume);
14624
14625   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14626   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14627 }
14628
14629 static void PlayLevelSound(int x, int y, int nr)
14630 {
14631   int sx = SCREENX(x), sy = SCREENY(y);
14632   int volume, stereo_position;
14633   int max_distance = 8;
14634   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14635
14636   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14637       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14638     return;
14639
14640   if (!IN_LEV_FIELD(x, y) ||
14641       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14642       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14643     return;
14644
14645   volume = SOUND_MAX_VOLUME;
14646
14647   if (!IN_SCR_FIELD(sx, sy))
14648   {
14649     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14650     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14651
14652     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14653   }
14654
14655   stereo_position = (SOUND_MAX_LEFT +
14656                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14657                      (SCR_FIELDX + 2 * max_distance));
14658
14659   if (IS_LOOP_SOUND(nr))
14660   {
14661     /* This assures that quieter loop sounds do not overwrite louder ones,
14662        while restarting sound volume comparison with each new game frame. */
14663
14664     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14665       return;
14666
14667     loop_sound_volume[nr] = volume;
14668     loop_sound_frame[nr] = FrameCounter;
14669   }
14670
14671   PlaySoundExt(nr, volume, stereo_position, type);
14672 }
14673
14674 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14675 {
14676   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14677                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14678                  y < LEVELY(BY1) ? LEVELY(BY1) :
14679                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14680                  sound_action);
14681 }
14682
14683 static void PlayLevelSoundAction(int x, int y, int action)
14684 {
14685   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14686 }
14687
14688 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14689 {
14690   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14691
14692   if (sound_effect != SND_UNDEFINED)
14693     PlayLevelSound(x, y, sound_effect);
14694 }
14695
14696 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14697                                               int action)
14698 {
14699   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14700
14701   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14702     PlayLevelSound(x, y, sound_effect);
14703 }
14704
14705 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14706 {
14707   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14708
14709   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14710     PlayLevelSound(x, y, sound_effect);
14711 }
14712
14713 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14714 {
14715   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14716
14717   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14718     StopSound(sound_effect);
14719 }
14720
14721 static void PlayLevelMusic()
14722 {
14723   if (levelset.music[level_nr] != MUS_UNDEFINED)
14724     PlayMusic(levelset.music[level_nr]);        /* from config file */
14725   else
14726     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14727 }
14728
14729 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14730 {
14731   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14732   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14733   int x = xx - 1 - offset;
14734   int y = yy - 1 - offset;
14735
14736   switch (sample)
14737   {
14738     case SAMPLE_blank:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14740       break;
14741
14742     case SAMPLE_roll:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14744       break;
14745
14746     case SAMPLE_stone:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14748       break;
14749
14750     case SAMPLE_nut:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14752       break;
14753
14754     case SAMPLE_crack:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14756       break;
14757
14758     case SAMPLE_bug:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14760       break;
14761
14762     case SAMPLE_tank:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14764       break;
14765
14766     case SAMPLE_android_clone:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14768       break;
14769
14770     case SAMPLE_android_move:
14771       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14772       break;
14773
14774     case SAMPLE_spring:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14776       break;
14777
14778     case SAMPLE_slurp:
14779       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14780       break;
14781
14782     case SAMPLE_eater:
14783       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14784       break;
14785
14786     case SAMPLE_eater_eat:
14787       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14788       break;
14789
14790     case SAMPLE_alien:
14791       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14792       break;
14793
14794     case SAMPLE_collect:
14795       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14796       break;
14797
14798     case SAMPLE_diamond:
14799       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14800       break;
14801
14802     case SAMPLE_squash:
14803       /* !!! CHECK THIS !!! */
14804 #if 1
14805       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14806 #else
14807       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14808 #endif
14809       break;
14810
14811     case SAMPLE_wonderfall:
14812       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14813       break;
14814
14815     case SAMPLE_drip:
14816       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14817       break;
14818
14819     case SAMPLE_push:
14820       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14821       break;
14822
14823     case SAMPLE_dirt:
14824       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14825       break;
14826
14827     case SAMPLE_acid:
14828       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14829       break;
14830
14831     case SAMPLE_ball:
14832       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14833       break;
14834
14835     case SAMPLE_grow:
14836       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14837       break;
14838
14839     case SAMPLE_wonder:
14840       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14841       break;
14842
14843     case SAMPLE_door:
14844       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14845       break;
14846
14847     case SAMPLE_exit_open:
14848       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14849       break;
14850
14851     case SAMPLE_exit_leave:
14852       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14853       break;
14854
14855     case SAMPLE_dynamite:
14856       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14857       break;
14858
14859     case SAMPLE_tick:
14860       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14861       break;
14862
14863     case SAMPLE_press:
14864       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14865       break;
14866
14867     case SAMPLE_wheel:
14868       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14869       break;
14870
14871     case SAMPLE_boom:
14872       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14873       break;
14874
14875     case SAMPLE_die:
14876       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14877       break;
14878
14879     case SAMPLE_time:
14880       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14881       break;
14882
14883     default:
14884       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14885       break;
14886   }
14887 }
14888
14889 #if 0
14890 void ChangeTime(int value)
14891 {
14892   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14893
14894   *time += value;
14895
14896   /* EMC game engine uses value from time counter of RND game engine */
14897   level.native_em_level->lev->time = *time;
14898
14899   DrawGameValue_Time(*time);
14900 }
14901
14902 void RaiseScore(int value)
14903 {
14904   /* EMC game engine and RND game engine have separate score counters */
14905   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14906                 &level.native_em_level->lev->score : &local_player->score);
14907
14908   *score += value;
14909
14910   DrawGameValue_Score(*score);
14911 }
14912 #endif
14913
14914 void RaiseScore(int value)
14915 {
14916   local_player->score += value;
14917
14918 #if 1
14919   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14920
14921   DisplayGameControlValues();
14922 #else
14923   DrawGameValue_Score(local_player->score);
14924 #endif
14925 }
14926
14927 void RaiseScoreElement(int element)
14928 {
14929   switch (element)
14930   {
14931     case EL_EMERALD:
14932     case EL_BD_DIAMOND:
14933     case EL_EMERALD_YELLOW:
14934     case EL_EMERALD_RED:
14935     case EL_EMERALD_PURPLE:
14936     case EL_SP_INFOTRON:
14937       RaiseScore(level.score[SC_EMERALD]);
14938       break;
14939     case EL_DIAMOND:
14940       RaiseScore(level.score[SC_DIAMOND]);
14941       break;
14942     case EL_CRYSTAL:
14943       RaiseScore(level.score[SC_CRYSTAL]);
14944       break;
14945     case EL_PEARL:
14946       RaiseScore(level.score[SC_PEARL]);
14947       break;
14948     case EL_BUG:
14949     case EL_BD_BUTTERFLY:
14950     case EL_SP_ELECTRON:
14951       RaiseScore(level.score[SC_BUG]);
14952       break;
14953     case EL_SPACESHIP:
14954     case EL_BD_FIREFLY:
14955     case EL_SP_SNIKSNAK:
14956       RaiseScore(level.score[SC_SPACESHIP]);
14957       break;
14958     case EL_YAMYAM:
14959     case EL_DARK_YAMYAM:
14960       RaiseScore(level.score[SC_YAMYAM]);
14961       break;
14962     case EL_ROBOT:
14963       RaiseScore(level.score[SC_ROBOT]);
14964       break;
14965     case EL_PACMAN:
14966       RaiseScore(level.score[SC_PACMAN]);
14967       break;
14968     case EL_NUT:
14969       RaiseScore(level.score[SC_NUT]);
14970       break;
14971     case EL_DYNAMITE:
14972     case EL_EM_DYNAMITE:
14973     case EL_SP_DISK_RED:
14974     case EL_DYNABOMB_INCREASE_NUMBER:
14975     case EL_DYNABOMB_INCREASE_SIZE:
14976     case EL_DYNABOMB_INCREASE_POWER:
14977       RaiseScore(level.score[SC_DYNAMITE]);
14978       break;
14979     case EL_SHIELD_NORMAL:
14980     case EL_SHIELD_DEADLY:
14981       RaiseScore(level.score[SC_SHIELD]);
14982       break;
14983     case EL_EXTRA_TIME:
14984       RaiseScore(level.extra_time_score);
14985       break;
14986     case EL_KEY_1:
14987     case EL_KEY_2:
14988     case EL_KEY_3:
14989     case EL_KEY_4:
14990     case EL_EM_KEY_1:
14991     case EL_EM_KEY_2:
14992     case EL_EM_KEY_3:
14993     case EL_EM_KEY_4:
14994     case EL_EMC_KEY_5:
14995     case EL_EMC_KEY_6:
14996     case EL_EMC_KEY_7:
14997     case EL_EMC_KEY_8:
14998     case EL_DC_KEY_WHITE:
14999       RaiseScore(level.score[SC_KEY]);
15000       break;
15001     default:
15002       RaiseScore(element_info[element].collect_score);
15003       break;
15004   }
15005 }
15006
15007 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15008 {
15009   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15010   {
15011 #if defined(NETWORK_AVALIABLE)
15012     if (options.network)
15013       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15014     else
15015 #endif
15016     {
15017       if (quick_quit)
15018       {
15019 #if 1
15020
15021 #if 1
15022         FadeSkipNextFadeIn();
15023 #else
15024         fading = fading_none;
15025 #endif
15026
15027 #else
15028         OpenDoor(DOOR_CLOSE_1);
15029 #endif
15030
15031         game_status = GAME_MODE_MAIN;
15032
15033 #if 1
15034         DrawAndFadeInMainMenu(REDRAW_FIELD);
15035 #else
15036         DrawMainMenu();
15037 #endif
15038       }
15039       else
15040       {
15041 #if 0
15042         FadeOut(REDRAW_FIELD);
15043 #endif
15044
15045         game_status = GAME_MODE_MAIN;
15046
15047         DrawAndFadeInMainMenu(REDRAW_FIELD);
15048       }
15049     }
15050   }
15051   else          /* continue playing the game */
15052   {
15053     if (tape.playing && tape.deactivate_display)
15054       TapeDeactivateDisplayOff(TRUE);
15055
15056     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15057
15058     if (tape.playing && tape.deactivate_display)
15059       TapeDeactivateDisplayOn();
15060   }
15061 }
15062
15063 void RequestQuitGame(boolean ask_if_really_quit)
15064 {
15065   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15066   boolean skip_request = AllPlayersGone || quick_quit;
15067
15068   RequestQuitGameExt(skip_request, quick_quit,
15069                      "Do you really want to quit the game ?");
15070 }
15071
15072
15073 /* ------------------------------------------------------------------------- */
15074 /* random generator functions                                                */
15075 /* ------------------------------------------------------------------------- */
15076
15077 unsigned int InitEngineRandom_RND(long seed)
15078 {
15079   game.num_random_calls = 0;
15080
15081 #if 0
15082   unsigned int rnd_seed = InitEngineRandom(seed);
15083
15084   printf("::: START RND: %d\n", rnd_seed);
15085
15086   return rnd_seed;
15087 #else
15088
15089   return InitEngineRandom(seed);
15090
15091 #endif
15092
15093 }
15094
15095 unsigned int RND(int max)
15096 {
15097   if (max > 0)
15098   {
15099     game.num_random_calls++;
15100
15101     return GetEngineRandom(max);
15102   }
15103
15104   return 0;
15105 }
15106
15107
15108 /* ------------------------------------------------------------------------- */
15109 /* game engine snapshot handling functions                                   */
15110 /* ------------------------------------------------------------------------- */
15111
15112 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15113
15114 struct EngineSnapshotInfo
15115 {
15116   /* runtime values for custom element collect score */
15117   int collect_score[NUM_CUSTOM_ELEMENTS];
15118
15119   /* runtime values for group element choice position */
15120   int choice_pos[NUM_GROUP_ELEMENTS];
15121
15122   /* runtime values for belt position animations */
15123   int belt_graphic[4 * NUM_BELT_PARTS];
15124   int belt_anim_mode[4 * NUM_BELT_PARTS];
15125 };
15126
15127 struct EngineSnapshotNodeInfo
15128 {
15129   void *buffer_orig;
15130   void *buffer_copy;
15131   int size;
15132 };
15133
15134 static struct EngineSnapshotInfo engine_snapshot_rnd;
15135 static ListNode *engine_snapshot_list = NULL;
15136 static char *snapshot_level_identifier = NULL;
15137 static int snapshot_level_nr = -1;
15138
15139 void FreeEngineSnapshot()
15140 {
15141   while (engine_snapshot_list != NULL)
15142     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15143                        checked_free);
15144
15145   setString(&snapshot_level_identifier, NULL);
15146   snapshot_level_nr = -1;
15147 }
15148
15149 static void SaveEngineSnapshotValues_RND()
15150 {
15151   static int belt_base_active_element[4] =
15152   {
15153     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15154     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15155     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15156     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15157   };
15158   int i, j;
15159
15160   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15161   {
15162     int element = EL_CUSTOM_START + i;
15163
15164     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15165   }
15166
15167   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15168   {
15169     int element = EL_GROUP_START + i;
15170
15171     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15172   }
15173
15174   for (i = 0; i < 4; i++)
15175   {
15176     for (j = 0; j < NUM_BELT_PARTS; j++)
15177     {
15178       int element = belt_base_active_element[i] + j;
15179       int graphic = el2img(element);
15180       int anim_mode = graphic_info[graphic].anim_mode;
15181
15182       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15183       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15184     }
15185   }
15186 }
15187
15188 static void LoadEngineSnapshotValues_RND()
15189 {
15190   unsigned long num_random_calls = game.num_random_calls;
15191   int i, j;
15192
15193   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15194   {
15195     int element = EL_CUSTOM_START + i;
15196
15197     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15198   }
15199
15200   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15201   {
15202     int element = EL_GROUP_START + i;
15203
15204     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15205   }
15206
15207   for (i = 0; i < 4; i++)
15208   {
15209     for (j = 0; j < NUM_BELT_PARTS; j++)
15210     {
15211       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15212       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15213
15214       graphic_info[graphic].anim_mode = anim_mode;
15215     }
15216   }
15217
15218   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15219   {
15220     InitRND(tape.random_seed);
15221     for (i = 0; i < num_random_calls; i++)
15222       RND(1);
15223   }
15224
15225   if (game.num_random_calls != num_random_calls)
15226   {
15227     Error(ERR_INFO, "number of random calls out of sync");
15228     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15229     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15230     Error(ERR_EXIT, "this should not happen -- please debug");
15231   }
15232 }
15233
15234 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15235 {
15236   struct EngineSnapshotNodeInfo *bi =
15237     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15238
15239   bi->buffer_orig = buffer;
15240   bi->buffer_copy = checked_malloc(size);
15241   bi->size = size;
15242
15243   memcpy(bi->buffer_copy, buffer, size);
15244
15245   addNodeToList(&engine_snapshot_list, NULL, bi);
15246 }
15247
15248 void SaveEngineSnapshot()
15249 {
15250   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15251
15252   if (level_editor_test_game)   /* do not save snapshots from editor */
15253     return;
15254
15255   /* copy some special values to a structure better suited for the snapshot */
15256
15257   SaveEngineSnapshotValues_RND();
15258   SaveEngineSnapshotValues_EM();
15259
15260   /* save values stored in special snapshot structure */
15261
15262   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15263   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15264
15265   /* save further RND engine values */
15266
15267   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15268   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15269   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15270
15271   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15272   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15273   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15274   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15275
15276   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15277   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15278   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15279   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15280   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15281
15282   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15283   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15284   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15285
15286   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15287
15288   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15289
15290   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15291   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15292
15293   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15294   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15295   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15296   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15297   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15298   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15299   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15300   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15301   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15302   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15303   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15304   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15305   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15306   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15307   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15308   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15309   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15310   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15311
15312   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15313   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15314
15315   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15316   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15317   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15318
15319   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15320   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15321
15322   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15323   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15324   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15325   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15326   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15327
15328   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15329   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15330
15331   /* save level identification information */
15332
15333   setString(&snapshot_level_identifier, leveldir_current->identifier);
15334   snapshot_level_nr = level_nr;
15335
15336 #if 0
15337   ListNode *node = engine_snapshot_list;
15338   int num_bytes = 0;
15339
15340   while (node != NULL)
15341   {
15342     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15343
15344     node = node->next;
15345   }
15346
15347   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15348 #endif
15349 }
15350
15351 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15352 {
15353   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15354 }
15355
15356 void LoadEngineSnapshot()
15357 {
15358   ListNode *node = engine_snapshot_list;
15359
15360   if (engine_snapshot_list == NULL)
15361     return;
15362
15363   while (node != NULL)
15364   {
15365     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15366
15367     node = node->next;
15368   }
15369
15370   /* restore special values from snapshot structure */
15371
15372   LoadEngineSnapshotValues_RND();
15373   LoadEngineSnapshotValues_EM();
15374 }
15375
15376 boolean CheckEngineSnapshot()
15377 {
15378   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15379           snapshot_level_nr == level_nr);
15380 }
15381
15382
15383 /* ---------- new game button stuff ---------------------------------------- */
15384
15385 /* graphic position values for game buttons */
15386 #define GAME_BUTTON_XSIZE       30
15387 #define GAME_BUTTON_YSIZE       30
15388 #define GAME_BUTTON_XPOS        5
15389 #define GAME_BUTTON_YPOS        215
15390 #define SOUND_BUTTON_XPOS       5
15391 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15392
15393 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15394 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15395 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15396 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15397 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15398 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15399
15400 static struct
15401 {
15402   int *x, *y;
15403   int gd_x, gd_y;
15404   int gadget_id;
15405   char *infotext;
15406 } gamebutton_info[NUM_GAME_BUTTONS] =
15407 {
15408 #if 1
15409   {
15410     &game.button.stop.x,        &game.button.stop.y,
15411     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15412     GAME_CTRL_ID_STOP,
15413     "stop game"
15414   },
15415   {
15416     &game.button.pause.x,       &game.button.pause.y,
15417     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15418     GAME_CTRL_ID_PAUSE,
15419     "pause game"
15420   },
15421   {
15422     &game.button.play.x,        &game.button.play.y,
15423     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15424     GAME_CTRL_ID_PLAY,
15425     "play game"
15426   },
15427   {
15428     &game.button.sound_music.x, &game.button.sound_music.y,
15429     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15430     SOUND_CTRL_ID_MUSIC,
15431     "background music on/off"
15432   },
15433   {
15434     &game.button.sound_loops.x, &game.button.sound_loops.y,
15435     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15436     SOUND_CTRL_ID_LOOPS,
15437     "sound loops on/off"
15438   },
15439   {
15440     &game.button.sound_simple.x,&game.button.sound_simple.y,
15441     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15442     SOUND_CTRL_ID_SIMPLE,
15443     "normal sounds on/off"
15444   }
15445 #else
15446   {
15447     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15448     GAME_CTRL_ID_STOP,
15449     "stop game"
15450   },
15451   {
15452     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15453     GAME_CTRL_ID_PAUSE,
15454     "pause game"
15455   },
15456   {
15457     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15458     GAME_CTRL_ID_PLAY,
15459     "play game"
15460   },
15461   {
15462     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15463     SOUND_CTRL_ID_MUSIC,
15464     "background music on/off"
15465   },
15466   {
15467     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15468     SOUND_CTRL_ID_LOOPS,
15469     "sound loops on/off"
15470   },
15471   {
15472     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15473     SOUND_CTRL_ID_SIMPLE,
15474     "normal sounds on/off"
15475   }
15476 #endif
15477 };
15478
15479 void CreateGameButtons()
15480 {
15481   int i;
15482
15483   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15484   {
15485     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15486     struct GadgetInfo *gi;
15487     int button_type;
15488     boolean checked;
15489     unsigned long event_mask;
15490     int x, y;
15491     int gd_xoffset, gd_yoffset;
15492     int gd_x1, gd_x2, gd_y1, gd_y2;
15493     int id = i;
15494
15495     x = DX + *gamebutton_info[i].x;
15496     y = DY + *gamebutton_info[i].y;
15497     gd_xoffset = gamebutton_info[i].gd_x;
15498     gd_yoffset = gamebutton_info[i].gd_y;
15499     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15500     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15501
15502     if (id == GAME_CTRL_ID_STOP ||
15503         id == GAME_CTRL_ID_PAUSE ||
15504         id == GAME_CTRL_ID_PLAY)
15505     {
15506       button_type = GD_TYPE_NORMAL_BUTTON;
15507       checked = FALSE;
15508       event_mask = GD_EVENT_RELEASED;
15509       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15510       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15511     }
15512     else
15513     {
15514       button_type = GD_TYPE_CHECK_BUTTON;
15515       checked =
15516         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15517          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15518          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15519       event_mask = GD_EVENT_PRESSED;
15520       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15521       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15522     }
15523
15524     gi = CreateGadget(GDI_CUSTOM_ID, id,
15525                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15526 #if 1
15527                       GDI_X, x,
15528                       GDI_Y, y,
15529 #else
15530                       GDI_X, DX + gd_xoffset,
15531                       GDI_Y, DY + gd_yoffset,
15532 #endif
15533                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15534                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15535                       GDI_TYPE, button_type,
15536                       GDI_STATE, GD_BUTTON_UNPRESSED,
15537                       GDI_CHECKED, checked,
15538                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15539                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15540                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15541                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15542                       GDI_DIRECT_DRAW, FALSE,
15543                       GDI_EVENT_MASK, event_mask,
15544                       GDI_CALLBACK_ACTION, HandleGameButtons,
15545                       GDI_END);
15546
15547     if (gi == NULL)
15548       Error(ERR_EXIT, "cannot create gadget");
15549
15550     game_gadget[id] = gi;
15551   }
15552 }
15553
15554 void FreeGameButtons()
15555 {
15556   int i;
15557
15558   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15559     FreeGadget(game_gadget[i]);
15560 }
15561
15562 static void MapGameButtons()
15563 {
15564   int i;
15565
15566   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15567     MapGadget(game_gadget[i]);
15568 }
15569
15570 void UnmapGameButtons()
15571 {
15572   int i;
15573
15574   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15575     UnmapGadget(game_gadget[i]);
15576 }
15577
15578 void RedrawGameButtons()
15579 {
15580   int i;
15581
15582   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15583     RedrawGadget(game_gadget[i]);
15584 }
15585
15586 static void HandleGameButtons(struct GadgetInfo *gi)
15587 {
15588   int id = gi->custom_id;
15589
15590   if (game_status != GAME_MODE_PLAYING)
15591     return;
15592
15593   switch (id)
15594   {
15595     case GAME_CTRL_ID_STOP:
15596       if (tape.playing)
15597         TapeStop();
15598       else
15599         RequestQuitGame(TRUE);
15600       break;
15601
15602     case GAME_CTRL_ID_PAUSE:
15603       if (options.network)
15604       {
15605 #if defined(NETWORK_AVALIABLE)
15606         if (tape.pausing)
15607           SendToServer_ContinuePlaying();
15608         else
15609           SendToServer_PausePlaying();
15610 #endif
15611       }
15612       else
15613         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15614       break;
15615
15616     case GAME_CTRL_ID_PLAY:
15617       if (tape.pausing)
15618       {
15619 #if defined(NETWORK_AVALIABLE)
15620         if (options.network)
15621           SendToServer_ContinuePlaying();
15622         else
15623 #endif
15624         {
15625           tape.pausing = FALSE;
15626           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15627         }
15628       }
15629       break;
15630
15631     case SOUND_CTRL_ID_MUSIC:
15632       if (setup.sound_music)
15633       { 
15634         setup.sound_music = FALSE;
15635         FadeMusic();
15636       }
15637       else if (audio.music_available)
15638       { 
15639         setup.sound = setup.sound_music = TRUE;
15640
15641         SetAudioMode(setup.sound);
15642
15643         PlayLevelMusic();
15644       }
15645       break;
15646
15647     case SOUND_CTRL_ID_LOOPS:
15648       if (setup.sound_loops)
15649         setup.sound_loops = FALSE;
15650       else if (audio.loops_available)
15651       {
15652         setup.sound = setup.sound_loops = TRUE;
15653         SetAudioMode(setup.sound);
15654       }
15655       break;
15656
15657     case SOUND_CTRL_ID_SIMPLE:
15658       if (setup.sound_simple)
15659         setup.sound_simple = FALSE;
15660       else if (audio.sound_available)
15661       {
15662         setup.sound = setup.sound_simple = TRUE;
15663         SetAudioMode(setup.sound);
15664       }
15665       break;
15666
15667     default:
15668       break;
15669   }
15670 }