rnd-20070407-2-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_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 GamePanelControlInfo
226 {
227   int nr;
228
229   struct TextPosInfo *pos;
230   int type;
231
232   int value, last_value;
233   int frame, last_frame;
234   int gfx_frame;
235 };
236
237 static struct GamePanelControlInfo game_panel_controls[] =
238 {
239   {
240     GAME_PANEL_LEVEL_NUMBER,
241     &game.panel.level_number,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_GEMS,
246     &game.panel.gems,
247     TYPE_INTEGER,
248   },
249   {
250     GAME_PANEL_INVENTORY_COUNT,
251     &game.panel.inventory_count,
252     TYPE_INTEGER,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_1,
256     &game.panel.inventory_first[0],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_2,
261     &game.panel.inventory_first[1],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_3,
266     &game.panel.inventory_first[2],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_4,
271     &game.panel.inventory_first[3],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_5,
276     &game.panel.inventory_first[4],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_6,
281     &game.panel.inventory_first[5],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_7,
286     &game.panel.inventory_first[6],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_8,
291     &game.panel.inventory_first[7],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_1,
296     &game.panel.inventory_last[0],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_2,
301     &game.panel.inventory_last[1],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_3,
306     &game.panel.inventory_last[2],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_4,
311     &game.panel.inventory_last[3],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_5,
316     &game.panel.inventory_last[4],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_6,
321     &game.panel.inventory_last[5],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_7,
326     &game.panel.inventory_last[6],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_8,
331     &game.panel.inventory_last[7],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_1,
336     &game.panel.key[0],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_2,
341     &game.panel.key[1],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_3,
346     &game.panel.key[2],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_4,
351     &game.panel.key[3],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_5,
356     &game.panel.key[4],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_6,
361     &game.panel.key[5],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_7,
366     &game.panel.key[6],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_8,
371     &game.panel.key[7],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_WHITE,
376     &game.panel.key_white,
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_WHITE_COUNT,
381     &game.panel.key_white_count,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_SCORE,
386     &game.panel.score,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME,
391     &game.panel.time,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_HH,
396     &game.panel.time_hh,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_MM,
401     &game.panel.time_mm,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_TIME_SS,
406     &game.panel.time_ss,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_CE_SCORE_1,
586     &game.panel.ce_score[0],
587     TYPE_INTEGER,
588   },
589   {
590     GAME_PANEL_CE_SCORE_2,
591     &game.panel.ce_score[1],
592     TYPE_INTEGER,
593   },
594   {
595     GAME_PANEL_CE_SCORE_3,
596     &game.panel.ce_score[2],
597     TYPE_INTEGER,
598   },
599   {
600     GAME_PANEL_CE_SCORE_4,
601     &game.panel.ce_score[3],
602     TYPE_INTEGER,
603   },
604   {
605     GAME_PANEL_CE_SCORE_5,
606     &game.panel.ce_score[4],
607     TYPE_INTEGER,
608   },
609   {
610     GAME_PANEL_CE_SCORE_6,
611     &game.panel.ce_score[5],
612     TYPE_INTEGER,
613   },
614   {
615     GAME_PANEL_CE_SCORE_7,
616     &game.panel.ce_score[6],
617     TYPE_INTEGER,
618   },
619   {
620     GAME_PANEL_CE_SCORE_8,
621     &game.panel.ce_score[7],
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_CE_SCORE_1_ELEMENT,
626     &game.panel.ce_score_element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_CE_SCORE_2_ELEMENT,
631     &game.panel.ce_score_element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_CE_SCORE_3_ELEMENT,
636     &game.panel.ce_score_element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_CE_SCORE_4_ELEMENT,
641     &game.panel.ce_score_element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_CE_SCORE_5_ELEMENT,
646     &game.panel.ce_score_element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_CE_SCORE_6_ELEMENT,
651     &game.panel.ce_score_element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_CE_SCORE_7_ELEMENT,
656     &game.panel.ce_score_element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_CE_SCORE_8_ELEMENT,
661     &game.panel.ce_score_element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_PLAYER_NAME,
666     &game.panel.player_name,
667     TYPE_STRING,
668   },
669   {
670     GAME_PANEL_LEVEL_NAME,
671     &game.panel.level_name,
672     TYPE_STRING,
673   },
674   {
675     GAME_PANEL_LEVEL_AUTHOR,
676     &game.panel.level_author,
677     TYPE_STRING,
678   },
679
680   {
681     -1,
682     NULL,
683     -1,
684   }
685 };
686 #endif
687
688
689 /* values for delayed check of falling and moving elements and for collision */
690 #define CHECK_DELAY_MOVING      3
691 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
692 #define CHECK_DELAY_COLLISION   2
693 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
694
695 /* values for initial player move delay (initial delay counter value) */
696 #define INITIAL_MOVE_DELAY_OFF  -1
697 #define INITIAL_MOVE_DELAY_ON   0
698
699 /* values for player movement speed (which is in fact a delay value) */
700 #define MOVE_DELAY_MIN_SPEED    32
701 #define MOVE_DELAY_NORMAL_SPEED 8
702 #define MOVE_DELAY_HIGH_SPEED   4
703 #define MOVE_DELAY_MAX_SPEED    1
704
705 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
706 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
707
708 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
709 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
710
711 /* values for other actions */
712 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
713 #define MOVE_STEPSIZE_MIN       (1)
714 #define MOVE_STEPSIZE_MAX       (TILEX)
715
716 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
717 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
718
719 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
720
721 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
722                                  RND(element_info[e].push_delay_random))
723 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
724                                  RND(element_info[e].drop_delay_random))
725 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
726                                  RND(element_info[e].move_delay_random))
727 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
728                                     (element_info[e].move_delay_random))
729 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
730                                  RND(element_info[e].ce_value_random_initial))
731 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
732 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
733                                  RND((c)->delay_random * (c)->delay_frames))
734 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
735                                  RND((c)->delay_random))
736
737
738 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
739          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
740
741 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
742         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
743          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
744          (be) + (e) - EL_SELF)
745
746 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
747         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
748          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
749          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
750          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
751          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
752          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
753          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
754          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
755          (e))
756
757 #define CAN_GROW_INTO(e)                                                \
758         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
759
760 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
761                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
762                                         (condition)))
763
764 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
765                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
766                                         (CAN_MOVE_INTO_ACID(e) &&       \
767                                          Feld[x][y] == EL_ACID) ||      \
768                                         (condition)))
769
770 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
771                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
772                                         (CAN_MOVE_INTO_ACID(e) &&       \
773                                          Feld[x][y] == EL_ACID) ||      \
774                                         (condition)))
775
776 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
777                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
778                                         (condition) ||                  \
779                                         (CAN_MOVE_INTO_ACID(e) &&       \
780                                          Feld[x][y] == EL_ACID) ||      \
781                                         (DONT_COLLIDE_WITH(e) &&        \
782                                          IS_PLAYER(x, y) &&             \
783                                          !PLAYER_ENEMY_PROTECTED(x, y))))
784
785 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
786         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
787
788 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
789         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
790
791 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
792         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
793
794 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
795         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
796                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
797
798 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
799         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
800
801 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
802         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
803
804 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
805         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
806
807 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
808         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
809
810 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
811         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
812
813 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
814         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
815                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
816                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
817                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
818                                                  IS_FOOD_PENGUIN(Feld[x][y])))
819 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
820         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
821
822 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
823         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
824
825 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
826         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
827
828 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
829         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
830                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
831
832 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
833
834 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
835                 (!IS_PLAYER(x, y) &&                                    \
836                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
837
838 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
839         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
840
841 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
842 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
843
844 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
845 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
846 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
847 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
848
849 /* game button identifiers */
850 #define GAME_CTRL_ID_STOP               0
851 #define GAME_CTRL_ID_PAUSE              1
852 #define GAME_CTRL_ID_PLAY               2
853 #define SOUND_CTRL_ID_MUSIC             3
854 #define SOUND_CTRL_ID_LOOPS             4
855 #define SOUND_CTRL_ID_SIMPLE            5
856
857 #define NUM_GAME_BUTTONS                6
858
859
860 /* forward declaration for internal use */
861
862 static void CreateField(int, int, int);
863
864 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
865 static void AdvanceFrameAndPlayerCounters(int);
866
867 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
868 static boolean MovePlayer(struct PlayerInfo *, int, int);
869 static void ScrollPlayer(struct PlayerInfo *, int);
870 static void ScrollScreen(struct PlayerInfo *, int);
871
872 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
873
874 static void InitBeltMovement(void);
875 static void CloseAllOpenTimegates(void);
876 static void CheckGravityMovement(struct PlayerInfo *);
877 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
878 static void KillPlayerUnlessEnemyProtected(int, int);
879 static void KillPlayerUnlessExplosionProtected(int, int);
880
881 static void TestIfPlayerTouchesCustomElement(int, int);
882 static void TestIfElementTouchesCustomElement(int, int);
883 static void TestIfElementHitsCustomElement(int, int, int);
884 #if 0
885 static void TestIfElementSmashesCustomElement(int, int, int);
886 #endif
887
888 static void HandleElementChange(int, int, int);
889 static void ExecuteCustomElementAction(int, int, int, int);
890 static boolean ChangeElement(int, int, int, int);
891
892 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
893 #define CheckTriggeredElementChange(x, y, e, ev)                        \
894         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
895 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
896         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
897 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
898         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
899 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
900         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
901
902 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
903 #define CheckElementChange(x, y, e, te, ev)                             \
904         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
905 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
906         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
907 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
908         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
909
910 static void PlayLevelSound(int, int, int);
911 static void PlayLevelSoundNearest(int, int, int);
912 static void PlayLevelSoundAction(int, int, int);
913 static void PlayLevelSoundElementAction(int, int, int, int);
914 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
915 static void PlayLevelSoundActionIfLoop(int, int, int);
916 static void StopLevelSoundActionIfLoop(int, int, int);
917 static void PlayLevelMusic();
918
919 static void MapGameButtons();
920 static void HandleGameButtons(struct GadgetInfo *);
921
922 int AmoebeNachbarNr(int, int);
923 void AmoebeUmwandeln(int, int);
924 void ContinueMoving(int, int);
925 void Bang(int, int);
926 void InitMovDir(int, int);
927 void InitAmoebaNr(int, int);
928 int NewHiScore(void);
929
930 void TestIfGoodThingHitsBadThing(int, int, int);
931 void TestIfBadThingHitsGoodThing(int, int, int);
932 void TestIfPlayerTouchesBadThing(int, int);
933 void TestIfPlayerRunsIntoBadThing(int, int, int);
934 void TestIfBadThingTouchesPlayer(int, int);
935 void TestIfBadThingRunsIntoPlayer(int, int, int);
936 void TestIfFriendTouchesBadThing(int, int);
937 void TestIfBadThingTouchesFriend(int, int);
938 void TestIfBadThingTouchesOtherBadThing(int, int);
939
940 void KillPlayer(struct PlayerInfo *);
941 void BuryPlayer(struct PlayerInfo *);
942 void RemovePlayer(struct PlayerInfo *);
943
944 boolean SnapField(struct PlayerInfo *, int, int);
945 boolean DropElement(struct PlayerInfo *);
946
947 static int getInvisibleActiveFromInvisibleElement(int);
948 static int getInvisibleFromInvisibleActiveElement(int);
949
950 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
951
952 /* for detection of endless loops, caused by custom element programming */
953 /* (using maximal playfield width x 10 is just a rough approximation) */
954 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
955
956 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
957 {                                                                       \
958   if (recursion_loop_detected)                                          \
959     return (rc);                                                        \
960                                                                         \
961   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
962   {                                                                     \
963     recursion_loop_detected = TRUE;                                     \
964     recursion_loop_element = (e);                                       \
965   }                                                                     \
966                                                                         \
967   recursion_loop_depth++;                                               \
968 }
969
970 #define RECURSION_LOOP_DETECTION_END()                                  \
971 {                                                                       \
972   recursion_loop_depth--;                                               \
973 }
974
975 static int recursion_loop_depth;
976 static boolean recursion_loop_detected;
977 static boolean recursion_loop_element;
978
979
980 /* ------------------------------------------------------------------------- */
981 /* definition of elements that automatically change to other elements after  */
982 /* a specified time, eventually calling a function when changing             */
983 /* ------------------------------------------------------------------------- */
984
985 /* forward declaration for changer functions */
986 static void InitBuggyBase(int, int);
987 static void WarnBuggyBase(int, int);
988
989 static void InitTrap(int, int);
990 static void ActivateTrap(int, int);
991 static void ChangeActiveTrap(int, int);
992
993 static void InitRobotWheel(int, int);
994 static void RunRobotWheel(int, int);
995 static void StopRobotWheel(int, int);
996
997 static void InitTimegateWheel(int, int);
998 static void RunTimegateWheel(int, int);
999
1000 static void InitMagicBallDelay(int, int);
1001 static void ActivateMagicBall(int, int);
1002
1003 struct ChangingElementInfo
1004 {
1005   int element;
1006   int target_element;
1007   int change_delay;
1008   void (*pre_change_function)(int x, int y);
1009   void (*change_function)(int x, int y);
1010   void (*post_change_function)(int x, int y);
1011 };
1012
1013 static struct ChangingElementInfo change_delay_list[] =
1014 {
1015   {
1016     EL_NUT_BREAKING,
1017     EL_EMERALD,
1018     6,
1019     NULL,
1020     NULL,
1021     NULL
1022   },
1023   {
1024     EL_PEARL_BREAKING,
1025     EL_EMPTY,
1026     8,
1027     NULL,
1028     NULL,
1029     NULL
1030   },
1031   {
1032     EL_EXIT_OPENING,
1033     EL_EXIT_OPEN,
1034     29,
1035     NULL,
1036     NULL,
1037     NULL
1038   },
1039   {
1040     EL_EXIT_CLOSING,
1041     EL_EXIT_CLOSED,
1042     29,
1043     NULL,
1044     NULL,
1045     NULL
1046   },
1047   {
1048     EL_STEEL_EXIT_OPENING,
1049     EL_STEEL_EXIT_OPEN,
1050     29,
1051     NULL,
1052     NULL,
1053     NULL
1054   },
1055   {
1056     EL_STEEL_EXIT_CLOSING,
1057     EL_STEEL_EXIT_CLOSED,
1058     29,
1059     NULL,
1060     NULL,
1061     NULL
1062   },
1063   {
1064     EL_EM_EXIT_OPENING,
1065     EL_EM_EXIT_OPEN,
1066     29,
1067     NULL,
1068     NULL,
1069     NULL
1070   },
1071   {
1072     EL_EM_EXIT_CLOSING,
1073 #if 1
1074     EL_EMPTY,
1075 #else
1076     EL_EM_EXIT_CLOSED,
1077 #endif
1078     29,
1079     NULL,
1080     NULL,
1081     NULL
1082   },
1083   {
1084     EL_EM_STEEL_EXIT_OPENING,
1085     EL_EM_STEEL_EXIT_OPEN,
1086     29,
1087     NULL,
1088     NULL,
1089     NULL
1090   },
1091   {
1092     EL_EM_STEEL_EXIT_CLOSING,
1093 #if 1
1094     EL_STEELWALL,
1095 #else
1096     EL_EM_STEEL_EXIT_CLOSED,
1097 #endif
1098     29,
1099     NULL,
1100     NULL,
1101     NULL
1102   },
1103   {
1104     EL_SP_EXIT_OPENING,
1105     EL_SP_EXIT_OPEN,
1106     29,
1107     NULL,
1108     NULL,
1109     NULL
1110   },
1111   {
1112     EL_SP_EXIT_CLOSING,
1113     EL_SP_EXIT_CLOSED,
1114     29,
1115     NULL,
1116     NULL,
1117     NULL
1118   },
1119   {
1120     EL_SWITCHGATE_OPENING,
1121     EL_SWITCHGATE_OPEN,
1122     29,
1123     NULL,
1124     NULL,
1125     NULL
1126   },
1127   {
1128     EL_SWITCHGATE_CLOSING,
1129     EL_SWITCHGATE_CLOSED,
1130     29,
1131     NULL,
1132     NULL,
1133     NULL
1134   },
1135   {
1136     EL_TIMEGATE_OPENING,
1137     EL_TIMEGATE_OPEN,
1138     29,
1139     NULL,
1140     NULL,
1141     NULL
1142   },
1143   {
1144     EL_TIMEGATE_CLOSING,
1145     EL_TIMEGATE_CLOSED,
1146     29,
1147     NULL,
1148     NULL,
1149     NULL
1150   },
1151
1152   {
1153     EL_ACID_SPLASH_LEFT,
1154     EL_EMPTY,
1155     8,
1156     NULL,
1157     NULL,
1158     NULL
1159   },
1160   {
1161     EL_ACID_SPLASH_RIGHT,
1162     EL_EMPTY,
1163     8,
1164     NULL,
1165     NULL,
1166     NULL
1167   },
1168   {
1169     EL_SP_BUGGY_BASE,
1170     EL_SP_BUGGY_BASE_ACTIVATING,
1171     0,
1172     InitBuggyBase,
1173     NULL,
1174     NULL
1175   },
1176   {
1177     EL_SP_BUGGY_BASE_ACTIVATING,
1178     EL_SP_BUGGY_BASE_ACTIVE,
1179     0,
1180     InitBuggyBase,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_SP_BUGGY_BASE_ACTIVE,
1186     EL_SP_BUGGY_BASE,
1187     0,
1188     InitBuggyBase,
1189     WarnBuggyBase,
1190     NULL
1191   },
1192   {
1193     EL_TRAP,
1194     EL_TRAP_ACTIVE,
1195     0,
1196     InitTrap,
1197     NULL,
1198     ActivateTrap
1199   },
1200   {
1201     EL_TRAP_ACTIVE,
1202     EL_TRAP,
1203     31,
1204     NULL,
1205     ChangeActiveTrap,
1206     NULL
1207   },
1208   {
1209     EL_ROBOT_WHEEL_ACTIVE,
1210     EL_ROBOT_WHEEL,
1211     0,
1212     InitRobotWheel,
1213     RunRobotWheel,
1214     StopRobotWheel
1215   },
1216   {
1217     EL_TIMEGATE_SWITCH_ACTIVE,
1218     EL_TIMEGATE_SWITCH,
1219     0,
1220     InitTimegateWheel,
1221     RunTimegateWheel,
1222     NULL
1223   },
1224   {
1225     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1226     EL_DC_TIMEGATE_SWITCH,
1227     0,
1228     InitTimegateWheel,
1229     RunTimegateWheel,
1230     NULL
1231   },
1232   {
1233     EL_EMC_MAGIC_BALL_ACTIVE,
1234     EL_EMC_MAGIC_BALL_ACTIVE,
1235     0,
1236     InitMagicBallDelay,
1237     NULL,
1238     ActivateMagicBall
1239   },
1240   {
1241     EL_EMC_SPRING_BUMPER_ACTIVE,
1242     EL_EMC_SPRING_BUMPER,
1243     8,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_DIAGONAL_SHRINKING,
1250     EL_UNDEFINED,
1251     0,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_DIAGONAL_GROWING,
1258     EL_UNDEFINED,
1259     0,
1260     NULL,
1261     NULL,
1262     NULL,
1263   },
1264
1265   {
1266     EL_UNDEFINED,
1267     EL_UNDEFINED,
1268     -1,
1269     NULL,
1270     NULL,
1271     NULL
1272   }
1273 };
1274
1275 struct
1276 {
1277   int element;
1278   int push_delay_fixed, push_delay_random;
1279 }
1280 push_delay_list[] =
1281 {
1282   { EL_SPRING,                  0, 0 },
1283   { EL_BALLOON,                 0, 0 },
1284
1285   { EL_SOKOBAN_OBJECT,          2, 0 },
1286   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1287   { EL_SATELLITE,               2, 0 },
1288   { EL_SP_DISK_YELLOW,          2, 0 },
1289
1290   { EL_UNDEFINED,               0, 0 },
1291 };
1292
1293 struct
1294 {
1295   int element;
1296   int move_stepsize;
1297 }
1298 move_stepsize_list[] =
1299 {
1300   { EL_AMOEBA_DROP,             2 },
1301   { EL_AMOEBA_DROPPING,         2 },
1302   { EL_QUICKSAND_FILLING,       1 },
1303   { EL_QUICKSAND_EMPTYING,      1 },
1304   { EL_QUICKSAND_FAST_FILLING,  2 },
1305   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1306   { EL_MAGIC_WALL_FILLING,      2 },
1307   { EL_MAGIC_WALL_EMPTYING,     2 },
1308   { EL_BD_MAGIC_WALL_FILLING,   2 },
1309   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1310   { EL_DC_MAGIC_WALL_FILLING,   2 },
1311   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1312
1313   { EL_UNDEFINED,               0 },
1314 };
1315
1316 struct
1317 {
1318   int element;
1319   int count;
1320 }
1321 collect_count_list[] =
1322 {
1323   { EL_EMERALD,                 1 },
1324   { EL_BD_DIAMOND,              1 },
1325   { EL_EMERALD_YELLOW,          1 },
1326   { EL_EMERALD_RED,             1 },
1327   { EL_EMERALD_PURPLE,          1 },
1328   { EL_DIAMOND,                 3 },
1329   { EL_SP_INFOTRON,             1 },
1330   { EL_PEARL,                   5 },
1331   { EL_CRYSTAL,                 8 },
1332
1333   { EL_UNDEFINED,               0 },
1334 };
1335
1336 struct
1337 {
1338   int element;
1339   int direction;
1340 }
1341 access_direction_list[] =
1342 {
1343   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1344   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1345   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1346   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1347   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1348   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1349   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1350   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1351   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1352   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1353   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1354
1355   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1356   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1357   { EL_SP_PORT_UP,                                                   MV_DOWN },
1358   { EL_SP_PORT_DOWN,                                         MV_UP           },
1359   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1360   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1361   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1362   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1363   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1364   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1365   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1366   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1367   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1368   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1369   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1370   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1371   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1372   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1373   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1374
1375   { EL_UNDEFINED,                       MV_NONE                              }
1376 };
1377
1378 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1379
1380 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1381 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1382 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1383                                  IS_JUST_CHANGING(x, y))
1384
1385 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1386
1387 /* static variables for playfield scan mode (scanning forward or backward) */
1388 static int playfield_scan_start_x = 0;
1389 static int playfield_scan_start_y = 0;
1390 static int playfield_scan_delta_x = 1;
1391 static int playfield_scan_delta_y = 1;
1392
1393 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1394                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1395                                      (y) += playfield_scan_delta_y)     \
1396                                 for ((x) = playfield_scan_start_x;      \
1397                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1398                                      (x) += playfield_scan_delta_x)
1399
1400 #ifdef DEBUG
1401 void DEBUG_SetMaximumDynamite()
1402 {
1403   int i;
1404
1405   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1406     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1407       local_player->inventory_element[local_player->inventory_size++] =
1408         EL_DYNAMITE;
1409 }
1410 #endif
1411
1412 static void InitPlayfieldScanModeVars()
1413 {
1414   if (game.use_reverse_scan_direction)
1415   {
1416     playfield_scan_start_x = lev_fieldx - 1;
1417     playfield_scan_start_y = lev_fieldy - 1;
1418
1419     playfield_scan_delta_x = -1;
1420     playfield_scan_delta_y = -1;
1421   }
1422   else
1423   {
1424     playfield_scan_start_x = 0;
1425     playfield_scan_start_y = 0;
1426
1427     playfield_scan_delta_x = 1;
1428     playfield_scan_delta_y = 1;
1429   }
1430 }
1431
1432 static void InitPlayfieldScanMode(int mode)
1433 {
1434   game.use_reverse_scan_direction =
1435     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1436
1437   InitPlayfieldScanModeVars();
1438 }
1439
1440 static int get_move_delay_from_stepsize(int move_stepsize)
1441 {
1442   move_stepsize =
1443     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1444
1445   /* make sure that stepsize value is always a power of 2 */
1446   move_stepsize = (1 << log_2(move_stepsize));
1447
1448   return TILEX / move_stepsize;
1449 }
1450
1451 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1452                                boolean init_game)
1453 {
1454   int player_nr = player->index_nr;
1455   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1456   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1457
1458   /* do no immediately change move delay -- the player might just be moving */
1459   player->move_delay_value_next = move_delay;
1460
1461   /* information if player can move must be set separately */
1462   player->cannot_move = cannot_move;
1463
1464   if (init_game)
1465   {
1466     player->move_delay       = game.initial_move_delay[player_nr];
1467     player->move_delay_value = game.initial_move_delay_value[player_nr];
1468
1469     player->move_delay_value_next = -1;
1470
1471     player->move_delay_reset_counter = 0;
1472   }
1473 }
1474
1475 void GetPlayerConfig()
1476 {
1477   GameFrameDelay = setup.game_frame_delay;
1478
1479   if (!audio.sound_available)
1480     setup.sound_simple = FALSE;
1481
1482   if (!audio.loops_available)
1483     setup.sound_loops = FALSE;
1484
1485   if (!audio.music_available)
1486     setup.sound_music = FALSE;
1487
1488   if (!video.fullscreen_available)
1489     setup.fullscreen = FALSE;
1490
1491   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1492
1493   SetAudioMode(setup.sound);
1494   InitJoysticks();
1495 }
1496
1497 int GetElementFromGroupElement(int element)
1498 {
1499   if (IS_GROUP_ELEMENT(element))
1500   {
1501     struct ElementGroupInfo *group = element_info[element].group;
1502     int last_anim_random_frame = gfx.anim_random_frame;
1503     int element_pos;
1504
1505     if (group->choice_mode == ANIM_RANDOM)
1506       gfx.anim_random_frame = RND(group->num_elements_resolved);
1507
1508     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1509                                     group->choice_mode, 0,
1510                                     group->choice_pos);
1511
1512     if (group->choice_mode == ANIM_RANDOM)
1513       gfx.anim_random_frame = last_anim_random_frame;
1514
1515     group->choice_pos++;
1516
1517     element = group->element_resolved[element_pos];
1518   }
1519
1520   return element;
1521 }
1522
1523 static void InitPlayerField(int x, int y, int element, boolean init_game)
1524 {
1525   if (element == EL_SP_MURPHY)
1526   {
1527     if (init_game)
1528     {
1529       if (stored_player[0].present)
1530       {
1531         Feld[x][y] = EL_SP_MURPHY_CLONE;
1532
1533         return;
1534       }
1535       else
1536       {
1537         stored_player[0].use_murphy = TRUE;
1538
1539         if (!level.use_artwork_element[0])
1540           stored_player[0].artwork_element = EL_SP_MURPHY;
1541       }
1542
1543       Feld[x][y] = EL_PLAYER_1;
1544     }
1545   }
1546
1547   if (init_game)
1548   {
1549     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1550     int jx = player->jx, jy = player->jy;
1551
1552     player->present = TRUE;
1553
1554     player->block_last_field = (element == EL_SP_MURPHY ?
1555                                 level.sp_block_last_field :
1556                                 level.block_last_field);
1557
1558     /* ---------- initialize player's last field block delay --------------- */
1559
1560     /* always start with reliable default value (no adjustment needed) */
1561     player->block_delay_adjustment = 0;
1562
1563     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1564     if (player->block_last_field && element == EL_SP_MURPHY)
1565       player->block_delay_adjustment = 1;
1566
1567     /* special case 2: in game engines before 3.1.1, blocking was different */
1568     if (game.use_block_last_field_bug)
1569       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1570
1571     if (!options.network || player->connected)
1572     {
1573       player->active = TRUE;
1574
1575       /* remove potentially duplicate players */
1576       if (StorePlayer[jx][jy] == Feld[x][y])
1577         StorePlayer[jx][jy] = 0;
1578
1579       StorePlayer[x][y] = Feld[x][y];
1580
1581       if (options.debug)
1582       {
1583         printf("Player %d activated.\n", player->element_nr);
1584         printf("[Local player is %d and currently %s.]\n",
1585                local_player->element_nr,
1586                local_player->active ? "active" : "not active");
1587       }
1588     }
1589
1590     Feld[x][y] = EL_EMPTY;
1591
1592     player->jx = player->last_jx = x;
1593     player->jy = player->last_jy = y;
1594   }
1595 }
1596
1597 static void InitField(int x, int y, boolean init_game)
1598 {
1599   int element = Feld[x][y];
1600
1601   switch (element)
1602   {
1603     case EL_SP_MURPHY:
1604     case EL_PLAYER_1:
1605     case EL_PLAYER_2:
1606     case EL_PLAYER_3:
1607     case EL_PLAYER_4:
1608       InitPlayerField(x, y, element, init_game);
1609       break;
1610
1611     case EL_SOKOBAN_FIELD_PLAYER:
1612       element = Feld[x][y] = EL_PLAYER_1;
1613       InitField(x, y, init_game);
1614
1615       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1616       InitField(x, y, init_game);
1617       break;
1618
1619     case EL_SOKOBAN_FIELD_EMPTY:
1620       local_player->sokobanfields_still_needed++;
1621       break;
1622
1623     case EL_STONEBLOCK:
1624       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1625         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1626       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1627         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1628       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1629         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1630       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1631         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1632       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1633         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1634       break;
1635
1636     case EL_BUG:
1637     case EL_BUG_RIGHT:
1638     case EL_BUG_UP:
1639     case EL_BUG_LEFT:
1640     case EL_BUG_DOWN:
1641     case EL_SPACESHIP:
1642     case EL_SPACESHIP_RIGHT:
1643     case EL_SPACESHIP_UP:
1644     case EL_SPACESHIP_LEFT:
1645     case EL_SPACESHIP_DOWN:
1646     case EL_BD_BUTTERFLY:
1647     case EL_BD_BUTTERFLY_RIGHT:
1648     case EL_BD_BUTTERFLY_UP:
1649     case EL_BD_BUTTERFLY_LEFT:
1650     case EL_BD_BUTTERFLY_DOWN:
1651     case EL_BD_FIREFLY:
1652     case EL_BD_FIREFLY_RIGHT:
1653     case EL_BD_FIREFLY_UP:
1654     case EL_BD_FIREFLY_LEFT:
1655     case EL_BD_FIREFLY_DOWN:
1656     case EL_PACMAN_RIGHT:
1657     case EL_PACMAN_UP:
1658     case EL_PACMAN_LEFT:
1659     case EL_PACMAN_DOWN:
1660     case EL_YAMYAM:
1661     case EL_YAMYAM_LEFT:
1662     case EL_YAMYAM_RIGHT:
1663     case EL_YAMYAM_UP:
1664     case EL_YAMYAM_DOWN:
1665     case EL_DARK_YAMYAM:
1666     case EL_ROBOT:
1667     case EL_PACMAN:
1668     case EL_SP_SNIKSNAK:
1669     case EL_SP_ELECTRON:
1670     case EL_MOLE:
1671     case EL_MOLE_LEFT:
1672     case EL_MOLE_RIGHT:
1673     case EL_MOLE_UP:
1674     case EL_MOLE_DOWN:
1675       InitMovDir(x, y);
1676       break;
1677
1678     case EL_AMOEBA_FULL:
1679     case EL_BD_AMOEBA:
1680       InitAmoebaNr(x, y);
1681       break;
1682
1683     case EL_AMOEBA_DROP:
1684       if (y == lev_fieldy - 1)
1685       {
1686         Feld[x][y] = EL_AMOEBA_GROWING;
1687         Store[x][y] = EL_AMOEBA_WET;
1688       }
1689       break;
1690
1691     case EL_DYNAMITE_ACTIVE:
1692     case EL_SP_DISK_RED_ACTIVE:
1693     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1694     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1695     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1696     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1697       MovDelay[x][y] = 96;
1698       break;
1699
1700     case EL_EM_DYNAMITE_ACTIVE:
1701       MovDelay[x][y] = 32;
1702       break;
1703
1704     case EL_LAMP:
1705       local_player->lights_still_needed++;
1706       break;
1707
1708     case EL_PENGUIN:
1709       local_player->friends_still_needed++;
1710       break;
1711
1712     case EL_PIG:
1713     case EL_DRAGON:
1714       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1715       break;
1716
1717     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1718     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1719     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1720     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1721     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1722     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1723     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1724     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1725     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1726     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1727     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1728     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1729       if (init_game)
1730       {
1731         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1732         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1733         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1734
1735         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1736         {
1737           game.belt_dir[belt_nr] = belt_dir;
1738           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1739         }
1740         else    /* more than one switch -- set it like the first switch */
1741         {
1742           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1743         }
1744       }
1745       break;
1746
1747 #if !USE_BOTH_SWITCHGATE_SWITCHES
1748     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1749       if (init_game)
1750         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1751       break;
1752
1753     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1754       if (init_game)
1755         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1756       break;
1757 #endif
1758
1759     case EL_LIGHT_SWITCH_ACTIVE:
1760       if (init_game)
1761         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1762       break;
1763
1764     case EL_INVISIBLE_STEELWALL:
1765     case EL_INVISIBLE_WALL:
1766     case EL_INVISIBLE_SAND:
1767       if (game.light_time_left > 0 ||
1768           game.lenses_time_left > 0)
1769         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1770       break;
1771
1772     case EL_EMC_MAGIC_BALL:
1773       if (game.ball_state)
1774         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1775       break;
1776
1777     case EL_EMC_MAGIC_BALL_SWITCH:
1778       if (game.ball_state)
1779         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1780       break;
1781
1782     default:
1783       if (IS_CUSTOM_ELEMENT(element))
1784       {
1785         if (CAN_MOVE(element))
1786           InitMovDir(x, y);
1787
1788 #if USE_NEW_CUSTOM_VALUE
1789         if (!element_info[element].use_last_ce_value || init_game)
1790           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1791 #endif
1792       }
1793       else if (IS_GROUP_ELEMENT(element))
1794       {
1795         Feld[x][y] = GetElementFromGroupElement(element);
1796
1797         InitField(x, y, init_game);
1798       }
1799
1800       break;
1801   }
1802
1803   if (!init_game)
1804     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1805 }
1806
1807 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1808 {
1809   InitField(x, y, init_game);
1810
1811   /* not needed to call InitMovDir() -- already done by InitField()! */
1812   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1813       CAN_MOVE(Feld[x][y]))
1814     InitMovDir(x, y);
1815 }
1816
1817 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1818 {
1819   int old_element = Feld[x][y];
1820
1821   InitField(x, y, init_game);
1822
1823   /* not needed to call InitMovDir() -- already done by InitField()! */
1824   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1825       CAN_MOVE(old_element) &&
1826       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1827     InitMovDir(x, y);
1828
1829   /* this case is in fact a combination of not less than three bugs:
1830      first, it calls InitMovDir() for elements that can move, although this is
1831      already done by InitField(); then, it checks the element that was at this
1832      field _before_ the call to InitField() (which can change it); lastly, it
1833      was not called for "mole with direction" elements, which were treated as
1834      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1835   */
1836 }
1837
1838 #if 1
1839
1840 static int get_key_element_from_nr(int key_nr)
1841 {
1842   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1843                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1844                           EL_EM_KEY_1 : EL_KEY_1);
1845
1846   return key_base_element + key_nr;
1847 }
1848
1849 static int get_next_dropped_element(struct PlayerInfo *player)
1850 {
1851   return (player->inventory_size > 0 ?
1852           player->inventory_element[player->inventory_size - 1] :
1853           player->inventory_infinite_element != EL_UNDEFINED ?
1854           player->inventory_infinite_element :
1855           player->dynabombs_left > 0 ?
1856           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1857           EL_UNDEFINED);
1858 }
1859
1860 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1861 {
1862   /* pos >= 0: get element from bottom of the stack;
1863      pos <  0: get element from top of the stack */
1864
1865   if (pos < 0)
1866   {
1867     int min_inventory_size = -pos;
1868     int inventory_pos = player->inventory_size - min_inventory_size;
1869     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1870
1871     return (player->inventory_size >= min_inventory_size ?
1872             player->inventory_element[inventory_pos] :
1873             player->inventory_infinite_element != EL_UNDEFINED ?
1874             player->inventory_infinite_element :
1875             player->dynabombs_left >= min_dynabombs_left ?
1876             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1877             EL_UNDEFINED);
1878   }
1879   else
1880   {
1881     int min_dynabombs_left = pos + 1;
1882     int min_inventory_size = pos + 1 - player->dynabombs_left;
1883     int inventory_pos = pos - player->dynabombs_left;
1884
1885     return (player->inventory_infinite_element != EL_UNDEFINED ?
1886             player->inventory_infinite_element :
1887             player->dynabombs_left >= min_dynabombs_left ?
1888             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1889             player->inventory_size >= min_inventory_size ?
1890             player->inventory_element[inventory_pos] :
1891             EL_UNDEFINED);
1892   }
1893 }
1894
1895 void InitGameControlValues()
1896 {
1897   int i;
1898
1899   for (i = 0; game_panel_controls[i].nr != -1; i++)
1900   {
1901     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
1902     int nr = gpc->nr;
1903     int type = gpc->type;
1904     struct TextPosInfo *pos = gpc->pos;
1905
1906     if (nr != i)
1907     {
1908       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
1909       Error(ERR_EXIT, "this should not happen -- please debug");
1910     }
1911
1912     /* force update of game controls after initialization */
1913     gpc->value = gpc->last_value = -1;
1914     gpc->frame = gpc->last_frame = -1;
1915     gpc->gfx_frame = -1;
1916
1917     /* determine panel value width for later calculation of alignment */
1918     if (type == TYPE_INTEGER || type == TYPE_STRING)
1919     {
1920       pos->width = pos->size * getFontWidth(pos->font);
1921       pos->height = getFontHeight(pos->font);
1922     }
1923     else if (type == TYPE_ELEMENT)
1924     {
1925       pos->width = pos->size;
1926       pos->height = pos->size;
1927     }
1928   }
1929 }
1930
1931 void UpdateGameControlValues()
1932 {
1933   int i, k;
1934   int time = (level.time == 0 ? TimePlayed : TimeLeft);
1935   int score = (local_player->LevelSolved ? local_player->score_final :
1936                local_player->score);
1937   int exit_closed = (local_player->gems_still_needed > 0 ||
1938                      local_player->sokobanfields_still_needed > 0 ||
1939                      local_player->lights_still_needed > 0);
1940
1941   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
1942   game_panel_controls[GAME_PANEL_GEMS].value =
1943     local_player->gems_still_needed;
1944
1945   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
1946   for (i = 0; i < MAX_NUM_KEYS; i++)
1947     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
1948   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
1949   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
1950
1951   if (game.centered_player_nr == -1)
1952   {
1953     for (i = 0; i < MAX_PLAYERS; i++)
1954     {
1955       for (k = 0; k < MAX_NUM_KEYS; k++)
1956         if (stored_player[i].key[k])
1957           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1958             get_key_element_from_nr(k);
1959
1960       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1961         stored_player[i].inventory_size;
1962
1963       if (stored_player[i].num_white_keys > 0)
1964         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
1965           EL_DC_KEY_WHITE;
1966
1967       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1968         stored_player[i].num_white_keys;
1969     }
1970   }
1971   else
1972   {
1973     int player_nr = game.centered_player_nr;
1974
1975     for (k = 0; k < MAX_NUM_KEYS; k++)
1976       if (stored_player[player_nr].key[k])
1977         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
1978           get_key_element_from_nr(k);
1979
1980     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
1981       stored_player[player_nr].inventory_size;
1982
1983     if (stored_player[player_nr].num_white_keys > 0)
1984       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
1985
1986     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
1987       stored_player[player_nr].num_white_keys;
1988   }
1989
1990   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
1991   {
1992     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
1993       get_inventory_element_from_pos(local_player, i);
1994     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
1995       get_inventory_element_from_pos(local_player, -i - 1);
1996   }
1997
1998   game_panel_controls[GAME_PANEL_SCORE].value = score;
1999
2000   game_panel_controls[GAME_PANEL_TIME].value = time;
2001
2002   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2003   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2004   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2005
2006   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2007     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2008      EL_EMPTY);
2009   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2010     local_player->shield_normal_time_left;
2011   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2012     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2013      EL_EMPTY);
2014   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2015     local_player->shield_deadly_time_left;
2016
2017   game_panel_controls[GAME_PANEL_EXIT].value =
2018     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2019
2020   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2021     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2022   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2023     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2024      EL_EMC_MAGIC_BALL_SWITCH);
2025
2026   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2027     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2028   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2029     game.light_time_left;
2030
2031   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2032     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2033   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2034     game.timegate_time_left;
2035
2036   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2037     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2038
2039   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2040     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2041   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2042     game.lenses_time_left;
2043
2044   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2045     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2046   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2047     game.magnify_time_left;
2048
2049   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2050     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2051      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2052      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2053      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2054      EL_BALLOON_SWITCH_NONE);
2055
2056   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2057     local_player->dynabomb_count;
2058   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2059     local_player->dynabomb_size;
2060   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2061     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2062
2063   game_panel_controls[GAME_PANEL_PENGUINS].value =
2064     local_player->friends_still_needed;
2065
2066   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2067     local_player->sokobanfields_still_needed;
2068   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2069     local_player->sokobanfields_still_needed;
2070
2071   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2072     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2073
2074   for (i = 0; i < NUM_BELTS; i++)
2075   {
2076     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2077       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2078        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2079     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2080       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2081   }
2082
2083   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2084     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2085   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2086     game.magic_wall_time_left;
2087
2088 #if USE_PLAYER_GRAVITY
2089   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2090     local_player->gravity;
2091 #else
2092   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2093 #endif
2094
2095   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2096   {
2097     if (game.panel.ce_score[i].id != EL_UNDEFINED)
2098     {
2099       int ce_score = element_info[game.panel.ce_score[i].id].collect_score;
2100
2101       game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = ce_score;
2102       game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = ce_score;
2103     }
2104     else
2105     {
2106       game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value = 0;
2107       game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value = 0;
2108     }
2109   }
2110
2111   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2112   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2113   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2114
2115   for (i = 0; game_panel_controls[i].nr != -1; i++)
2116   {
2117     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2118
2119     if (gpc->type == TYPE_ELEMENT)
2120     {
2121       if (gpc->value != gpc->last_value)
2122         gpc->gfx_frame = 0;
2123       else
2124         gpc->gfx_frame++;
2125
2126       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2127                                             gpc->gfx_frame);
2128     }
2129   }
2130 }
2131
2132 void DisplayGameControlValues()
2133 {
2134   int i;
2135
2136   game_status = GAME_MODE_PSEUDO_PANEL;
2137
2138   for (i = 0; game_panel_controls[i].nr != -1; i++)
2139   {
2140     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2141     int nr = gpc->nr;
2142     int type = gpc->type;
2143     struct TextPosInfo *pos = gpc->pos;
2144     int value = gpc->value;
2145     int frame = gpc->frame;
2146     int last_value = gpc->last_value;
2147     int last_frame = gpc->last_frame;
2148     int size = pos->size;
2149     int font = pos->font;
2150
2151     if (value == last_value && frame == last_frame)
2152       continue;
2153
2154     gpc->last_value = value;
2155     gpc->last_frame = frame;
2156
2157 #if 0
2158     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2159 #endif
2160
2161     if (PANEL_DEACTIVATED(pos))
2162       continue;
2163
2164     if (type == TYPE_INTEGER)
2165     {
2166       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2167           nr == GAME_PANEL_TIME)
2168       {
2169         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2170
2171         if (use_dynamic_size)           /* use dynamic number of digits */
2172         {
2173           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2174           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2175           int size2 = size1 + 1;
2176           int font1 = pos->font;
2177           int font2 = pos->font_alt;
2178
2179           size = (value < value_change ? size1 : size2);
2180           font = (value < value_change ? font1 : font2);
2181
2182           /* clear background if value just changed its size (dynamic digits) */
2183           if ((last_value < value_change) != (value < value_change))
2184           {
2185             int width1 = size1 * getFontWidth(font1);
2186             int width2 = size2 * getFontWidth(font2);
2187             int max_width = MAX(width1, width2);
2188             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2189
2190             pos->width = max_width;
2191
2192             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2193                                        max_width, max_height);
2194           }
2195         }
2196       }
2197
2198 #if 1
2199       /* correct text size if "digits" is zero or less */
2200       if (size <= 0)
2201         size = strlen(int2str(value, size));
2202
2203       /* dynamically correct text alignment */
2204       pos->width = size * getFontWidth(font);
2205 #endif
2206
2207       DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, size), font);
2208     }
2209     else if (type == TYPE_ELEMENT)
2210     {
2211       int element, graphic;
2212       Bitmap *src_bitmap;
2213       int src_x, src_y;
2214       int width, height;
2215       int dst_x = PANEL_XPOS(pos);
2216       int dst_y = PANEL_YPOS(pos);
2217
2218       if (value == EL_UNDEFINED || value == EL_EMPTY)
2219       {
2220         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2221         graphic = el2panelimg(element);
2222
2223         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2224         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2225         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2226       }
2227       else
2228       {
2229         element = value;
2230         graphic = el2panelimg(value);
2231
2232         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2233       }
2234
2235       width  = graphic_info[graphic].width  * size / TILESIZE;
2236       height = graphic_info[graphic].height * size / TILESIZE;
2237
2238       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2239     }
2240     else if (type == TYPE_STRING)
2241     {
2242       boolean active = (value != 0);
2243       char *state_normal = "off";
2244       char *state_active = "on";
2245       char *state = (active ? state_active : state_normal);
2246       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2247                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2248                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2249                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2250
2251       if (nr == GAME_PANEL_GRAVITY_STATE)
2252       {
2253         int font1 = pos->font;          /* (used for normal state) */
2254         int font2 = pos->font_alt;      /* (used for active state) */
2255         int size1 = strlen(state_normal);
2256         int size2 = strlen(state_active);
2257         int width1 = size1 * getFontWidth(font1);
2258         int width2 = size2 * getFontWidth(font2);
2259         int max_width = MAX(width1, width2);
2260         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2261
2262         pos->width = max_width;
2263
2264         /* clear background for values that may have changed its size */
2265         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2266                                    max_width, max_height);
2267
2268         font = (active ? font2 : font1);
2269       }
2270
2271       if (s != NULL)
2272       {
2273         char *s_cut;
2274
2275 #if 1
2276         if (size <= 0)
2277         {
2278           /* don't truncate output if "chars" is zero or less */
2279           size = strlen(s);
2280
2281           /* dynamically correct text alignment */
2282           pos->width = size * getFontWidth(font);
2283         }
2284 #endif
2285
2286         s_cut = getStringCopyN(s, size);
2287
2288         DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), s_cut, font);
2289
2290         free(s_cut);
2291       }
2292     }
2293
2294     redraw_mask |= REDRAW_DOOR_1;
2295   }
2296
2297   game_status = GAME_MODE_PLAYING;
2298 }
2299
2300 void DrawGameValue_Emeralds(int value)
2301 {
2302   struct TextPosInfo *pos = &game.panel.gems;
2303 #if 1
2304   int font_nr = pos->font;
2305 #else
2306   int font_nr = FONT_TEXT_2;
2307 #endif
2308   int font_width = getFontWidth(font_nr);
2309   int chars = pos->size;
2310
2311 #if 1
2312   return;       /* !!! USE NEW STUFF !!! */
2313 #endif
2314
2315   if (PANEL_DEACTIVATED(pos))
2316     return;
2317
2318   pos->width = chars * font_width;
2319
2320   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2321 }
2322
2323 void DrawGameValue_Dynamite(int value)
2324 {
2325   struct TextPosInfo *pos = &game.panel.inventory_count;
2326 #if 1
2327   int font_nr = pos->font;
2328 #else
2329   int font_nr = FONT_TEXT_2;
2330 #endif
2331   int font_width = getFontWidth(font_nr);
2332   int chars = pos->size;
2333
2334 #if 1
2335   return;       /* !!! USE NEW STUFF !!! */
2336 #endif
2337
2338   if (PANEL_DEACTIVATED(pos))
2339     return;
2340
2341   pos->width = chars * font_width;
2342
2343   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2344 }
2345
2346 void DrawGameValue_Score(int value)
2347 {
2348   struct TextPosInfo *pos = &game.panel.score;
2349 #if 1
2350   int font_nr = pos->font;
2351 #else
2352   int font_nr = FONT_TEXT_2;
2353 #endif
2354   int font_width = getFontWidth(font_nr);
2355   int chars = pos->size;
2356
2357 #if 1
2358   return;       /* !!! USE NEW STUFF !!! */
2359 #endif
2360
2361   if (PANEL_DEACTIVATED(pos))
2362     return;
2363
2364   pos->width = chars * font_width;
2365
2366   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2367 }
2368
2369 void DrawGameValue_Time(int value)
2370 {
2371   struct TextPosInfo *pos = &game.panel.time;
2372   static int last_value = -1;
2373   int chars1 = 3;
2374   int chars2 = 4;
2375   int chars = pos->size;
2376 #if 1
2377   int font1_nr = pos->font;
2378   int font2_nr = pos->font_alt;
2379 #else
2380   int font1_nr = FONT_TEXT_2;
2381   int font2_nr = FONT_TEXT_1;
2382 #endif
2383   int font_nr = font1_nr;
2384   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2385
2386 #if 1
2387   return;       /* !!! USE NEW STUFF !!! */
2388 #endif
2389
2390   if (PANEL_DEACTIVATED(pos))
2391     return;
2392
2393   if (use_dynamic_chars)                /* use dynamic number of chars */
2394   {
2395     chars   = (value < 1000 ? chars1   : chars2);
2396     font_nr = (value < 1000 ? font1_nr : font2_nr);
2397   }
2398
2399   /* clear background if value just changed its size (dynamic chars only) */
2400   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2401   {
2402     int width1 = chars1 * getFontWidth(font1_nr);
2403     int width2 = chars2 * getFontWidth(font2_nr);
2404     int max_width = MAX(width1, width2);
2405     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2406
2407     pos->width = max_width;
2408
2409     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2410                                max_width, max_height);
2411   }
2412
2413   pos->width = chars * getFontWidth(font_nr);
2414
2415   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2416
2417   last_value = value;
2418 }
2419
2420 void DrawGameValue_Level(int value)
2421 {
2422   struct TextPosInfo *pos = &game.panel.level_number;
2423   int chars1 = 2;
2424   int chars2 = 3;
2425   int chars = pos->size;
2426 #if 1
2427   int font1_nr = pos->font;
2428   int font2_nr = pos->font_alt;
2429 #else
2430   int font1_nr = FONT_TEXT_2;
2431   int font2_nr = FONT_TEXT_1;
2432 #endif
2433   int font_nr = font1_nr;
2434   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2435
2436 #if 1
2437   return;       /* !!! USE NEW STUFF !!! */
2438 #endif
2439
2440   if (PANEL_DEACTIVATED(pos))
2441     return;
2442
2443   if (use_dynamic_chars)                /* use dynamic number of chars */
2444   {
2445     chars   = (level_nr < 100 ? chars1   : chars2);
2446     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2447   }
2448
2449   pos->width = chars * getFontWidth(font_nr);
2450
2451   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2452 }
2453
2454 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2455 {
2456 #if 0
2457   struct TextPosInfo *pos = &game.panel.keys;
2458 #endif
2459 #if 0
2460   int base_key_graphic = EL_KEY_1;
2461 #endif
2462   int i;
2463
2464 #if 1
2465   return;       /* !!! USE NEW STUFF !!! */
2466 #endif
2467
2468 #if 0
2469   if (PANEL_DEACTIVATED(pos))
2470     return;
2471 #endif
2472
2473 #if 0
2474   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2475     base_key_graphic = EL_EM_KEY_1;
2476 #endif
2477
2478 #if 0
2479   pos->width = 4 * MINI_TILEX;
2480 #endif
2481
2482 #if 1
2483   for (i = 0; i < MAX_NUM_KEYS; i++)
2484 #else
2485   /* currently only 4 of 8 possible keys are displayed */
2486   for (i = 0; i < STD_NUM_KEYS; i++)
2487 #endif
2488   {
2489 #if 1
2490     struct TextPosInfo *pos = &game.panel.key[i];
2491 #endif
2492     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2493     int src_y = DOOR_GFX_PAGEY1 + 123;
2494 #if 1
2495     int dst_x = PANEL_XPOS(pos);
2496     int dst_y = PANEL_YPOS(pos);
2497 #else
2498     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2499     int dst_y = PANEL_YPOS(pos);
2500 #endif
2501
2502 #if 1
2503     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2504                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2505                    EL_KEY_1) + i;
2506     int graphic = el2edimg(element);
2507 #endif
2508
2509 #if 1
2510     if (PANEL_DEACTIVATED(pos))
2511       continue;
2512 #endif
2513
2514 #if 0
2515     /* masked blit with tiles from half-size scaled bitmap does not work yet
2516        (no mask bitmap created for these sizes after loading and scaling) --
2517        solution: load without creating mask, scale, then create final mask */
2518
2519     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2520                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2521
2522     if (key[i])
2523     {
2524 #if 0
2525       int graphic = el2edimg(base_key_graphic + i);
2526 #endif
2527       Bitmap *src_bitmap;
2528       int src_x, src_y;
2529
2530       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2531
2532       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2533                     dst_x - src_x, dst_y - src_y);
2534       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2535                        dst_x, dst_y);
2536     }
2537 #else
2538 #if 1
2539     if (key[i])
2540       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2541     else
2542       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2543                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2544 #else
2545     if (key[i])
2546       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2547     else
2548       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2549                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2550 #endif
2551 #endif
2552   }
2553 }
2554
2555 #else
2556
2557 void DrawGameValue_Emeralds(int value)
2558 {
2559   int font_nr = FONT_TEXT_2;
2560   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2561
2562   if (PANEL_DEACTIVATED(game.panel.gems))
2563     return;
2564
2565   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2566 }
2567
2568 void DrawGameValue_Dynamite(int value)
2569 {
2570   int font_nr = FONT_TEXT_2;
2571   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2572
2573   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2574     return;
2575
2576   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2577 }
2578
2579 void DrawGameValue_Score(int value)
2580 {
2581   int font_nr = FONT_TEXT_2;
2582   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2583
2584   if (PANEL_DEACTIVATED(game.panel.score))
2585     return;
2586
2587   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2588 }
2589
2590 void DrawGameValue_Time(int value)
2591 {
2592   int font1_nr = FONT_TEXT_2;
2593 #if 1
2594   int font2_nr = FONT_TEXT_1;
2595 #else
2596   int font2_nr = FONT_LEVEL_NUMBER;
2597 #endif
2598   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2599   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2600
2601   if (PANEL_DEACTIVATED(game.panel.time))
2602     return;
2603
2604   /* clear background if value just changed its size */
2605   if (value == 999 || value == 1000)
2606     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2607
2608   if (value < 1000)
2609     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2610   else
2611     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2612 }
2613
2614 void DrawGameValue_Level(int value)
2615 {
2616   int font1_nr = FONT_TEXT_2;
2617 #if 1
2618   int font2_nr = FONT_TEXT_1;
2619 #else
2620   int font2_nr = FONT_LEVEL_NUMBER;
2621 #endif
2622
2623   if (PANEL_DEACTIVATED(game.panel.level))
2624     return;
2625
2626   if (level_nr < 100)
2627     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2628   else
2629     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2630 }
2631
2632 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2633 {
2634   int base_key_graphic = EL_KEY_1;
2635   int i;
2636
2637   if (PANEL_DEACTIVATED(game.panel.keys))
2638     return;
2639
2640   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2641     base_key_graphic = EL_EM_KEY_1;
2642
2643   /* currently only 4 of 8 possible keys are displayed */
2644   for (i = 0; i < STD_NUM_KEYS; i++)
2645   {
2646     int x = XX_KEYS + i * MINI_TILEX;
2647     int y = YY_KEYS;
2648
2649     if (key[i])
2650       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2651     else
2652       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2653                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2654   }
2655 }
2656
2657 #endif
2658
2659 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2660                        int key_bits)
2661 {
2662   int key[MAX_NUM_KEYS];
2663   int i;
2664
2665   /* prevent EM engine from updating time/score values parallel to GameWon() */
2666   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2667       local_player->LevelSolved)
2668     return;
2669
2670   for (i = 0; i < MAX_NUM_KEYS; i++)
2671     key[i] = key_bits & (1 << i);
2672
2673   DrawGameValue_Level(level_nr);
2674
2675   DrawGameValue_Emeralds(emeralds);
2676   DrawGameValue_Dynamite(dynamite);
2677   DrawGameValue_Score(score);
2678   DrawGameValue_Time(time);
2679
2680   DrawGameValue_Keys(key);
2681 }
2682
2683 void UpdateGameDoorValues()
2684 {
2685   UpdateGameControlValues();
2686 }
2687
2688 void DrawGameDoorValues()
2689 {
2690   DisplayGameControlValues();
2691 }
2692
2693 void DrawGameDoorValues_OLD()
2694 {
2695   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2696   int dynamite_value = 0;
2697   int score_value = (local_player->LevelSolved ? local_player->score_final :
2698                      local_player->score);
2699   int gems_value = local_player->gems_still_needed;
2700   int key_bits = 0;
2701   int i, j;
2702
2703   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2704   {
2705     DrawGameDoorValues_EM();
2706
2707     return;
2708   }
2709
2710   if (game.centered_player_nr == -1)
2711   {
2712     for (i = 0; i < MAX_PLAYERS; i++)
2713     {
2714       for (j = 0; j < MAX_NUM_KEYS; j++)
2715         if (stored_player[i].key[j])
2716           key_bits |= (1 << j);
2717
2718       dynamite_value += stored_player[i].inventory_size;
2719     }
2720   }
2721   else
2722   {
2723     int player_nr = game.centered_player_nr;
2724
2725     for (i = 0; i < MAX_NUM_KEYS; i++)
2726       if (stored_player[player_nr].key[i])
2727         key_bits |= (1 << i);
2728
2729     dynamite_value = stored_player[player_nr].inventory_size;
2730   }
2731
2732   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
2733                     key_bits);
2734 }
2735
2736
2737 /*
2738   =============================================================================
2739   InitGameEngine()
2740   -----------------------------------------------------------------------------
2741   initialize game engine due to level / tape version number
2742   =============================================================================
2743 */
2744
2745 static void InitGameEngine()
2746 {
2747   int i, j, k, l, x, y;
2748
2749   /* set game engine from tape file when re-playing, else from level file */
2750   game.engine_version = (tape.playing ? tape.engine_version :
2751                          level.game_version);
2752
2753   /* ---------------------------------------------------------------------- */
2754   /* set flags for bugs and changes according to active game engine version */
2755   /* ---------------------------------------------------------------------- */
2756
2757   /*
2758     Summary of bugfix/change:
2759     Fixed handling for custom elements that change when pushed by the player.
2760
2761     Fixed/changed in version:
2762     3.1.0
2763
2764     Description:
2765     Before 3.1.0, custom elements that "change when pushing" changed directly
2766     after the player started pushing them (until then handled in "DigField()").
2767     Since 3.1.0, these custom elements are not changed until the "pushing"
2768     move of the element is finished (now handled in "ContinueMoving()").
2769
2770     Affected levels/tapes:
2771     The first condition is generally needed for all levels/tapes before version
2772     3.1.0, which might use the old behaviour before it was changed; known tapes
2773     that are affected are some tapes from the level set "Walpurgis Gardens" by
2774     Jamie Cullen.
2775     The second condition is an exception from the above case and is needed for
2776     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2777     above (including some development versions of 3.1.0), but before it was
2778     known that this change would break tapes like the above and was fixed in
2779     3.1.1, so that the changed behaviour was active although the engine version
2780     while recording maybe was before 3.1.0. There is at least one tape that is
2781     affected by this exception, which is the tape for the one-level set "Bug
2782     Machine" by Juergen Bonhagen.
2783   */
2784
2785   game.use_change_when_pushing_bug =
2786     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2787      !(tape.playing &&
2788        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2789        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2790
2791   /*
2792     Summary of bugfix/change:
2793     Fixed handling for blocking the field the player leaves when moving.
2794
2795     Fixed/changed in version:
2796     3.1.1
2797
2798     Description:
2799     Before 3.1.1, when "block last field when moving" was enabled, the field
2800     the player is leaving when moving was blocked for the time of the move,
2801     and was directly unblocked afterwards. This resulted in the last field
2802     being blocked for exactly one less than the number of frames of one player
2803     move. Additionally, even when blocking was disabled, the last field was
2804     blocked for exactly one frame.
2805     Since 3.1.1, due to changes in player movement handling, the last field
2806     is not blocked at all when blocking is disabled. When blocking is enabled,
2807     the last field is blocked for exactly the number of frames of one player
2808     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2809     last field is blocked for exactly one more than the number of frames of
2810     one player move.
2811
2812     Affected levels/tapes:
2813     (!!! yet to be determined -- probably many !!!)
2814   */
2815
2816   game.use_block_last_field_bug =
2817     (game.engine_version < VERSION_IDENT(3,1,1,0));
2818
2819   /*
2820     Summary of bugfix/change:
2821     Changed behaviour of CE changes with multiple changes per single frame.
2822
2823     Fixed/changed in version:
2824     3.2.0-6
2825
2826     Description:
2827     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
2828     This resulted in race conditions where CEs seem to behave strange in some
2829     situations (where triggered CE changes were just skipped because there was
2830     already a CE change on that tile in the playfield in that engine frame).
2831     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
2832     (The number of changes per frame must be limited in any case, because else
2833     it is easily possible to define CE changes that would result in an infinite
2834     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
2835     should be set large enough so that it would only be reached in cases where
2836     the corresponding CE change conditions run into a loop. Therefore, it seems
2837     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
2838     maximal number of change pages for custom elements.)
2839
2840     Affected levels/tapes:
2841     Probably many.
2842   */
2843
2844 #if USE_ONLY_ONE_CHANGE_PER_FRAME
2845   game.max_num_changes_per_frame = 1;
2846 #else
2847   game.max_num_changes_per_frame =
2848     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
2849 #endif
2850
2851   /* ---------------------------------------------------------------------- */
2852
2853   /* default scan direction: scan playfield from top/left to bottom/right */
2854   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2855
2856   /* dynamically adjust element properties according to game engine version */
2857   InitElementPropertiesEngine(game.engine_version);
2858
2859 #if 0
2860   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2861   printf("          tape version == %06d [%s] [file: %06d]\n",
2862          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2863          tape.file_version);
2864   printf("       => game.engine_version == %06d\n", game.engine_version);
2865 #endif
2866
2867   /* ---------- initialize player's initial move delay --------------------- */
2868
2869   /* dynamically adjust player properties according to level information */
2870   for (i = 0; i < MAX_PLAYERS; i++)
2871     game.initial_move_delay_value[i] =
2872       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2873
2874   /* dynamically adjust player properties according to game engine version */
2875   for (i = 0; i < MAX_PLAYERS; i++)
2876     game.initial_move_delay[i] =
2877       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2878        game.initial_move_delay_value[i] : 0);
2879
2880   /* ---------- initialize player's initial push delay --------------------- */
2881
2882   /* dynamically adjust player properties according to game engine version */
2883   game.initial_push_delay_value =
2884     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2885
2886   /* ---------- initialize changing elements ------------------------------- */
2887
2888   /* initialize changing elements information */
2889   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2890   {
2891     struct ElementInfo *ei = &element_info[i];
2892
2893     /* this pointer might have been changed in the level editor */
2894     ei->change = &ei->change_page[0];
2895
2896     if (!IS_CUSTOM_ELEMENT(i))
2897     {
2898       ei->change->target_element = EL_EMPTY_SPACE;
2899       ei->change->delay_fixed = 0;
2900       ei->change->delay_random = 0;
2901       ei->change->delay_frames = 1;
2902     }
2903
2904     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2905     {
2906       ei->has_change_event[j] = FALSE;
2907
2908       ei->event_page_nr[j] = 0;
2909       ei->event_page[j] = &ei->change_page[0];
2910     }
2911   }
2912
2913   /* add changing elements from pre-defined list */
2914   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2915   {
2916     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2917     struct ElementInfo *ei = &element_info[ch_delay->element];
2918
2919     ei->change->target_element       = ch_delay->target_element;
2920     ei->change->delay_fixed          = ch_delay->change_delay;
2921
2922     ei->change->pre_change_function  = ch_delay->pre_change_function;
2923     ei->change->change_function      = ch_delay->change_function;
2924     ei->change->post_change_function = ch_delay->post_change_function;
2925
2926     ei->change->can_change = TRUE;
2927     ei->change->can_change_or_has_action = TRUE;
2928
2929     ei->has_change_event[CE_DELAY] = TRUE;
2930
2931     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2932     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2933   }
2934
2935   /* ---------- initialize internal run-time variables ------------- */
2936
2937   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2938   {
2939     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2940
2941     for (j = 0; j < ei->num_change_pages; j++)
2942     {
2943       ei->change_page[j].can_change_or_has_action =
2944         (ei->change_page[j].can_change |
2945          ei->change_page[j].has_action);
2946     }
2947   }
2948
2949   /* add change events from custom element configuration */
2950   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2951   {
2952     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2953
2954     for (j = 0; j < ei->num_change_pages; j++)
2955     {
2956       if (!ei->change_page[j].can_change_or_has_action)
2957         continue;
2958
2959       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2960       {
2961         /* only add event page for the first page found with this event */
2962         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2963         {
2964           ei->has_change_event[k] = TRUE;
2965
2966           ei->event_page_nr[k] = j;
2967           ei->event_page[k] = &ei->change_page[j];
2968         }
2969       }
2970     }
2971   }
2972
2973   /* ---------- initialize run-time trigger player and element ------------- */
2974
2975   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2976   {
2977     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2978
2979     for (j = 0; j < ei->num_change_pages; j++)
2980     {
2981       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2982       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
2983       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2984       ei->change_page[j].actual_trigger_ce_value = 0;
2985       ei->change_page[j].actual_trigger_ce_score = 0;
2986     }
2987   }
2988
2989   /* ---------- initialize trigger events ---------------------------------- */
2990
2991   /* initialize trigger events information */
2992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2994       trigger_events[i][j] = FALSE;
2995
2996   /* add trigger events from element change event properties */
2997   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2998   {
2999     struct ElementInfo *ei = &element_info[i];
3000
3001     for (j = 0; j < ei->num_change_pages; j++)
3002     {
3003       if (!ei->change_page[j].can_change_or_has_action)
3004         continue;
3005
3006       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3007       {
3008         int trigger_element = ei->change_page[j].trigger_element;
3009
3010         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3011         {
3012           if (ei->change_page[j].has_event[k])
3013           {
3014             if (IS_GROUP_ELEMENT(trigger_element))
3015             {
3016               struct ElementGroupInfo *group =
3017                 element_info[trigger_element].group;
3018
3019               for (l = 0; l < group->num_elements_resolved; l++)
3020                 trigger_events[group->element_resolved[l]][k] = TRUE;
3021             }
3022             else if (trigger_element == EL_ANY_ELEMENT)
3023               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3024                 trigger_events[l][k] = TRUE;
3025             else
3026               trigger_events[trigger_element][k] = TRUE;
3027           }
3028         }
3029       }
3030     }
3031   }
3032
3033   /* ---------- initialize push delay -------------------------------------- */
3034
3035   /* initialize push delay values to default */
3036   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3037   {
3038     if (!IS_CUSTOM_ELEMENT(i))
3039     {
3040       /* set default push delay values (corrected since version 3.0.7-1) */
3041       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3042       {
3043         element_info[i].push_delay_fixed = 2;
3044         element_info[i].push_delay_random = 8;
3045       }
3046       else
3047       {
3048         element_info[i].push_delay_fixed = 8;
3049         element_info[i].push_delay_random = 8;
3050       }
3051     }
3052   }
3053
3054   /* set push delay value for certain elements from pre-defined list */
3055   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3056   {
3057     int e = push_delay_list[i].element;
3058
3059     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3060     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3061   }
3062
3063   /* set push delay value for Supaplex elements for newer engine versions */
3064   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3065   {
3066     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3067     {
3068       if (IS_SP_ELEMENT(i))
3069       {
3070         /* set SP push delay to just enough to push under a falling zonk */
3071         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3072
3073         element_info[i].push_delay_fixed  = delay;
3074         element_info[i].push_delay_random = 0;
3075       }
3076     }
3077   }
3078
3079   /* ---------- initialize move stepsize ----------------------------------- */
3080
3081   /* initialize move stepsize values to default */
3082   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3083     if (!IS_CUSTOM_ELEMENT(i))
3084       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3085
3086   /* set move stepsize value for certain elements from pre-defined list */
3087   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3088   {
3089     int e = move_stepsize_list[i].element;
3090
3091     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3092   }
3093
3094   /* ---------- initialize collect score ----------------------------------- */
3095
3096   /* initialize collect score values for custom elements from initial value */
3097   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3098     if (IS_CUSTOM_ELEMENT(i))
3099       element_info[i].collect_score = element_info[i].collect_score_initial;
3100
3101   /* ---------- initialize collect count ----------------------------------- */
3102
3103   /* initialize collect count values for non-custom elements */
3104   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3105     if (!IS_CUSTOM_ELEMENT(i))
3106       element_info[i].collect_count_initial = 0;
3107
3108   /* add collect count values for all elements from pre-defined list */
3109   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3110     element_info[collect_count_list[i].element].collect_count_initial =
3111       collect_count_list[i].count;
3112
3113   /* ---------- initialize access direction -------------------------------- */
3114
3115   /* initialize access direction values to default (access from every side) */
3116   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3117     if (!IS_CUSTOM_ELEMENT(i))
3118       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3119
3120   /* set access direction value for certain elements from pre-defined list */
3121   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3122     element_info[access_direction_list[i].element].access_direction =
3123       access_direction_list[i].direction;
3124
3125   /* ---------- initialize explosion content ------------------------------- */
3126   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3127   {
3128     if (IS_CUSTOM_ELEMENT(i))
3129       continue;
3130
3131     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3132     {
3133       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3134
3135       element_info[i].content.e[x][y] =
3136         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3137          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3138          i == EL_PLAYER_3 ? EL_EMERALD :
3139          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3140          i == EL_MOLE ? EL_EMERALD_RED :
3141          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3142          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3143          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3144          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3145          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3146          i == EL_WALL_EMERALD ? EL_EMERALD :
3147          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3148          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3149          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3150          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3151          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3152          i == EL_WALL_PEARL ? EL_PEARL :
3153          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3154          EL_EMPTY);
3155     }
3156   }
3157
3158   /* ---------- initialize recursion detection ------------------------------ */
3159   recursion_loop_depth = 0;
3160   recursion_loop_detected = FALSE;
3161   recursion_loop_element = EL_UNDEFINED;
3162
3163   /* ---------- initialize graphics engine ---------------------------------- */
3164   game.scroll_delay_value =
3165     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3166      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3167   game.scroll_delay_value =
3168     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3169 }
3170
3171 int get_num_special_action(int element, int action_first, int action_last)
3172 {
3173   int num_special_action = 0;
3174   int i, j;
3175
3176   for (i = action_first; i <= action_last; i++)
3177   {
3178     boolean found = FALSE;
3179
3180     for (j = 0; j < NUM_DIRECTIONS; j++)
3181       if (el_act_dir2img(element, i, j) !=
3182           el_act_dir2img(element, ACTION_DEFAULT, j))
3183         found = TRUE;
3184
3185     if (found)
3186       num_special_action++;
3187     else
3188       break;
3189   }
3190
3191   return num_special_action;
3192 }
3193
3194
3195 /*
3196   =============================================================================
3197   InitGame()
3198   -----------------------------------------------------------------------------
3199   initialize and start new game
3200   =============================================================================
3201 */
3202
3203 void InitGame()
3204 {
3205   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3206   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3207   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3208 #if 0
3209   boolean do_fading = (game_status == GAME_MODE_MAIN);
3210 #endif
3211   int i, j, x, y;
3212
3213   game_status = GAME_MODE_PLAYING;
3214
3215   InitGameEngine();
3216   InitGameControlValues();
3217
3218   /* don't play tapes over network */
3219   network_playing = (options.network && !tape.playing);
3220
3221   for (i = 0; i < MAX_PLAYERS; i++)
3222   {
3223     struct PlayerInfo *player = &stored_player[i];
3224
3225     player->index_nr = i;
3226     player->index_bit = (1 << i);
3227     player->element_nr = EL_PLAYER_1 + i;
3228
3229     player->present = FALSE;
3230     player->active = FALSE;
3231     player->killed = FALSE;
3232
3233     player->action = 0;
3234     player->effective_action = 0;
3235     player->programmed_action = 0;
3236
3237     player->score = 0;
3238     player->score_final = 0;
3239
3240     player->gems_still_needed = level.gems_needed;
3241     player->sokobanfields_still_needed = 0;
3242     player->lights_still_needed = 0;
3243     player->friends_still_needed = 0;
3244
3245     for (j = 0; j < MAX_NUM_KEYS; j++)
3246       player->key[j] = FALSE;
3247
3248     player->num_white_keys = 0;
3249
3250     player->dynabomb_count = 0;
3251     player->dynabomb_size = 1;
3252     player->dynabombs_left = 0;
3253     player->dynabomb_xl = FALSE;
3254
3255     player->MovDir = MV_NONE;
3256     player->MovPos = 0;
3257     player->GfxPos = 0;
3258     player->GfxDir = MV_NONE;
3259     player->GfxAction = ACTION_DEFAULT;
3260     player->Frame = 0;
3261     player->StepFrame = 0;
3262
3263     player->use_murphy = FALSE;
3264     player->artwork_element =
3265       (level.use_artwork_element[i] ? level.artwork_element[i] :
3266        player->element_nr);
3267
3268     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3269     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3270
3271     player->gravity = level.initial_player_gravity[i];
3272
3273     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3274
3275     player->actual_frame_counter = 0;
3276
3277     player->step_counter = 0;
3278
3279     player->last_move_dir = MV_NONE;
3280
3281     player->is_active = FALSE;
3282
3283     player->is_waiting = FALSE;
3284     player->is_moving = FALSE;
3285     player->is_auto_moving = FALSE;
3286     player->is_digging = FALSE;
3287     player->is_snapping = FALSE;
3288     player->is_collecting = FALSE;
3289     player->is_pushing = FALSE;
3290     player->is_switching = FALSE;
3291     player->is_dropping = FALSE;
3292     player->is_dropping_pressed = FALSE;
3293
3294     player->is_bored = FALSE;
3295     player->is_sleeping = FALSE;
3296
3297     player->frame_counter_bored = -1;
3298     player->frame_counter_sleeping = -1;
3299
3300     player->anim_delay_counter = 0;
3301     player->post_delay_counter = 0;
3302
3303     player->dir_waiting = MV_NONE;
3304     player->action_waiting = ACTION_DEFAULT;
3305     player->last_action_waiting = ACTION_DEFAULT;
3306     player->special_action_bored = ACTION_DEFAULT;
3307     player->special_action_sleeping = ACTION_DEFAULT;
3308
3309     player->switch_x = -1;
3310     player->switch_y = -1;
3311
3312     player->drop_x = -1;
3313     player->drop_y = -1;
3314
3315     player->show_envelope = 0;
3316
3317     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3318
3319     player->push_delay       = -1;      /* initialized when pushing starts */
3320     player->push_delay_value = game.initial_push_delay_value;
3321
3322     player->drop_delay = 0;
3323     player->drop_pressed_delay = 0;
3324
3325     player->last_jx = -1;
3326     player->last_jy = -1;
3327     player->jx = -1;
3328     player->jy = -1;
3329
3330     player->shield_normal_time_left = 0;
3331     player->shield_deadly_time_left = 0;
3332
3333     player->inventory_infinite_element = EL_UNDEFINED;
3334     player->inventory_size = 0;
3335
3336     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3337     SnapField(player, 0, 0);
3338
3339     player->LevelSolved = FALSE;
3340     player->GameOver = FALSE;
3341
3342     player->LevelSolved_GameWon = FALSE;
3343     player->LevelSolved_GameEnd = FALSE;
3344     player->LevelSolved_PanelOff = FALSE;
3345     player->LevelSolved_SaveTape = FALSE;
3346     player->LevelSolved_SaveScore = FALSE;
3347   }
3348
3349   network_player_action_received = FALSE;
3350
3351 #if defined(NETWORK_AVALIABLE)
3352   /* initial null action */
3353   if (network_playing)
3354     SendToServer_MovePlayer(MV_NONE);
3355 #endif
3356
3357   ZX = ZY = -1;
3358   ExitX = ExitY = -1;
3359
3360   FrameCounter = 0;
3361   TimeFrames = 0;
3362   TimePlayed = 0;
3363   TimeLeft = level.time;
3364   TapeTime = 0;
3365
3366   ScreenMovDir = MV_NONE;
3367   ScreenMovPos = 0;
3368   ScreenGfxPos = 0;
3369
3370   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3371
3372   AllPlayersGone = FALSE;
3373
3374   game.yamyam_content_nr = 0;
3375   game.robot_wheel_active = FALSE;
3376   game.magic_wall_active = FALSE;
3377   game.magic_wall_time_left = 0;
3378   game.light_time_left = 0;
3379   game.timegate_time_left = 0;
3380   game.switchgate_pos = 0;
3381   game.wind_direction = level.wind_direction_initial;
3382
3383 #if !USE_PLAYER_GRAVITY
3384   game.gravity = FALSE;
3385   game.explosions_delayed = TRUE;
3386 #endif
3387
3388   game.lenses_time_left = 0;
3389   game.magnify_time_left = 0;
3390
3391   game.ball_state = level.ball_state_initial;
3392   game.ball_content_nr = 0;
3393
3394   game.envelope_active = FALSE;
3395
3396   /* set focus to local player for network games, else to all players */
3397   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3398   game.centered_player_nr_next = game.centered_player_nr;
3399   game.set_centered_player = FALSE;
3400
3401   if (network_playing && tape.recording)
3402   {
3403     /* store client dependent player focus when recording network games */
3404     tape.centered_player_nr_next = game.centered_player_nr_next;
3405     tape.set_centered_player = TRUE;
3406   }
3407
3408   for (i = 0; i < NUM_BELTS; i++)
3409   {
3410     game.belt_dir[i] = MV_NONE;
3411     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3412   }
3413
3414   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3415     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3416
3417   SCAN_PLAYFIELD(x, y)
3418   {
3419     Feld[x][y] = level.field[x][y];
3420     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3421     ChangeDelay[x][y] = 0;
3422     ChangePage[x][y] = -1;
3423 #if USE_NEW_CUSTOM_VALUE
3424     CustomValue[x][y] = 0;              /* initialized in InitField() */
3425 #endif
3426     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3427     AmoebaNr[x][y] = 0;
3428     WasJustMoving[x][y] = 0;
3429     WasJustFalling[x][y] = 0;
3430     CheckCollision[x][y] = 0;
3431     CheckImpact[x][y] = 0;
3432     Stop[x][y] = FALSE;
3433     Pushed[x][y] = FALSE;
3434
3435     ChangeCount[x][y] = 0;
3436     ChangeEvent[x][y] = -1;
3437
3438     ExplodePhase[x][y] = 0;
3439     ExplodeDelay[x][y] = 0;
3440     ExplodeField[x][y] = EX_TYPE_NONE;
3441
3442     RunnerVisit[x][y] = 0;
3443     PlayerVisit[x][y] = 0;
3444
3445     GfxFrame[x][y] = 0;
3446     GfxRandom[x][y] = INIT_GFX_RANDOM();
3447     GfxElement[x][y] = EL_UNDEFINED;
3448     GfxAction[x][y] = ACTION_DEFAULT;
3449     GfxDir[x][y] = MV_NONE;
3450   }
3451
3452   SCAN_PLAYFIELD(x, y)
3453   {
3454     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3455       emulate_bd = FALSE;
3456     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3457       emulate_sb = FALSE;
3458     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3459       emulate_sp = FALSE;
3460
3461     InitField(x, y, TRUE);
3462   }
3463
3464   InitBeltMovement();
3465
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467   {
3468     struct PlayerInfo *player = &stored_player[i];
3469
3470     /* set number of special actions for bored and sleeping animation */
3471     player->num_special_action_bored =
3472       get_num_special_action(player->artwork_element,
3473                              ACTION_BORING_1, ACTION_BORING_LAST);
3474     player->num_special_action_sleeping =
3475       get_num_special_action(player->artwork_element,
3476                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3477   }
3478
3479   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3480                     emulate_sb ? EMU_SOKOBAN :
3481                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3482
3483 #if USE_NEW_ALL_SLIPPERY
3484   /* initialize type of slippery elements */
3485   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3486   {
3487     if (!IS_CUSTOM_ELEMENT(i))
3488     {
3489       /* default: elements slip down either to the left or right randomly */
3490       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3491
3492       /* SP style elements prefer to slip down on the left side */
3493       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3494         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3495
3496       /* BD style elements prefer to slip down on the left side */
3497       if (game.emulation == EMU_BOULDERDASH)
3498         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3499     }
3500   }
3501 #endif
3502
3503   /* initialize explosion and ignition delay */
3504   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3505   {
3506     if (!IS_CUSTOM_ELEMENT(i))
3507     {
3508       int num_phase = 8;
3509       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3510                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3511                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3512       int last_phase = (num_phase + 1) * delay;
3513       int half_phase = (num_phase / 2) * delay;
3514
3515       element_info[i].explosion_delay = last_phase - 1;
3516       element_info[i].ignition_delay = half_phase;
3517
3518       if (i == EL_BLACK_ORB)
3519         element_info[i].ignition_delay = 1;
3520     }
3521
3522 #if 0
3523     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3524       element_info[i].explosion_delay = 1;
3525
3526     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3527       element_info[i].ignition_delay = 1;
3528 #endif
3529   }
3530
3531   /* correct non-moving belts to start moving left */
3532   for (i = 0; i < NUM_BELTS; i++)
3533     if (game.belt_dir[i] == MV_NONE)
3534       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3535
3536   /* check if any connected player was not found in playfield */
3537   for (i = 0; i < MAX_PLAYERS; i++)
3538   {
3539     struct PlayerInfo *player = &stored_player[i];
3540
3541     if (player->connected && !player->present)
3542     {
3543       for (j = 0; j < MAX_PLAYERS; j++)
3544       {
3545         struct PlayerInfo *some_player = &stored_player[j];
3546         int jx = some_player->jx, jy = some_player->jy;
3547
3548         /* assign first free player found that is present in the playfield */
3549         if (some_player->present && !some_player->connected)
3550         {
3551           player->present = TRUE;
3552           player->active = TRUE;
3553
3554           some_player->present = FALSE;
3555           some_player->active = FALSE;
3556
3557           player->artwork_element = some_player->artwork_element;
3558
3559           player->block_last_field       = some_player->block_last_field;
3560           player->block_delay_adjustment = some_player->block_delay_adjustment;
3561
3562           StorePlayer[jx][jy] = player->element_nr;
3563           player->jx = player->last_jx = jx;
3564           player->jy = player->last_jy = jy;
3565
3566           break;
3567         }
3568       }
3569     }
3570   }
3571
3572   if (tape.playing)
3573   {
3574     /* when playing a tape, eliminate all players who do not participate */
3575
3576     for (i = 0; i < MAX_PLAYERS; i++)
3577     {
3578       if (stored_player[i].active && !tape.player_participates[i])
3579       {
3580         struct PlayerInfo *player = &stored_player[i];
3581         int jx = player->jx, jy = player->jy;
3582
3583         player->active = FALSE;
3584         StorePlayer[jx][jy] = 0;
3585         Feld[jx][jy] = EL_EMPTY;
3586       }
3587     }
3588   }
3589   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3590   {
3591     /* when in single player mode, eliminate all but the first active player */
3592
3593     for (i = 0; i < MAX_PLAYERS; i++)
3594     {
3595       if (stored_player[i].active)
3596       {
3597         for (j = i + 1; j < MAX_PLAYERS; j++)
3598         {
3599           if (stored_player[j].active)
3600           {
3601             struct PlayerInfo *player = &stored_player[j];
3602             int jx = player->jx, jy = player->jy;
3603
3604             player->active = FALSE;
3605             player->present = FALSE;
3606
3607             StorePlayer[jx][jy] = 0;
3608             Feld[jx][jy] = EL_EMPTY;
3609           }
3610         }
3611       }
3612     }
3613   }
3614
3615   /* when recording the game, store which players take part in the game */
3616   if (tape.recording)
3617   {
3618     for (i = 0; i < MAX_PLAYERS; i++)
3619       if (stored_player[i].active)
3620         tape.player_participates[i] = TRUE;
3621   }
3622
3623   if (options.debug)
3624   {
3625     for (i = 0; i < MAX_PLAYERS; i++)
3626     {
3627       struct PlayerInfo *player = &stored_player[i];
3628
3629       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3630              i+1,
3631              player->present,
3632              player->connected,
3633              player->active);
3634       if (local_player == player)
3635         printf("Player  %d is local player.\n", i+1);
3636     }
3637   }
3638
3639   if (BorderElement == EL_EMPTY)
3640   {
3641     SBX_Left = 0;
3642     SBX_Right = lev_fieldx - SCR_FIELDX;
3643     SBY_Upper = 0;
3644     SBY_Lower = lev_fieldy - SCR_FIELDY;
3645   }
3646   else
3647   {
3648     SBX_Left = -1;
3649     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3650     SBY_Upper = -1;
3651     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3652   }
3653
3654   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3655     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3656
3657   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3658     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3659
3660   /* if local player not found, look for custom element that might create
3661      the player (make some assumptions about the right custom element) */
3662   if (!local_player->present)
3663   {
3664     int start_x = 0, start_y = 0;
3665     int found_rating = 0;
3666     int found_element = EL_UNDEFINED;
3667     int player_nr = local_player->index_nr;
3668
3669     SCAN_PLAYFIELD(x, y)
3670     {
3671       int element = Feld[x][y];
3672       int content;
3673       int xx, yy;
3674       boolean is_player;
3675
3676       if (level.use_start_element[player_nr] &&
3677           level.start_element[player_nr] == element &&
3678           found_rating < 4)
3679       {
3680         start_x = x;
3681         start_y = y;
3682
3683         found_rating = 4;
3684         found_element = element;
3685       }
3686
3687       if (!IS_CUSTOM_ELEMENT(element))
3688         continue;
3689
3690       if (CAN_CHANGE(element))
3691       {
3692         for (i = 0; i < element_info[element].num_change_pages; i++)
3693         {
3694           /* check for player created from custom element as single target */
3695           content = element_info[element].change_page[i].target_element;
3696           is_player = ELEM_IS_PLAYER(content);
3697
3698           if (is_player && (found_rating < 3 ||
3699                             (found_rating == 3 && element < found_element)))
3700           {
3701             start_x = x;
3702             start_y = y;
3703
3704             found_rating = 3;
3705             found_element = element;
3706           }
3707         }
3708       }
3709
3710       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3711       {
3712         /* check for player created from custom element as explosion content */
3713         content = element_info[element].content.e[xx][yy];
3714         is_player = ELEM_IS_PLAYER(content);
3715
3716         if (is_player && (found_rating < 2 ||
3717                           (found_rating == 2 && element < found_element)))
3718         {
3719           start_x = x + xx - 1;
3720           start_y = y + yy - 1;
3721
3722           found_rating = 2;
3723           found_element = element;
3724         }
3725
3726         if (!CAN_CHANGE(element))
3727           continue;
3728
3729         for (i = 0; i < element_info[element].num_change_pages; i++)
3730         {
3731           /* check for player created from custom element as extended target */
3732           content =
3733             element_info[element].change_page[i].target_content.e[xx][yy];
3734
3735           is_player = ELEM_IS_PLAYER(content);
3736
3737           if (is_player && (found_rating < 1 ||
3738                             (found_rating == 1 && element < found_element)))
3739           {
3740             start_x = x + xx - 1;
3741             start_y = y + yy - 1;
3742
3743             found_rating = 1;
3744             found_element = element;
3745           }
3746         }
3747       }
3748     }
3749
3750     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3751                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3752                 start_x - MIDPOSX);
3753
3754     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3755                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3756                 start_y - MIDPOSY);
3757   }
3758   else
3759   {
3760     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3761                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3762                 local_player->jx - MIDPOSX);
3763
3764     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3765                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3766                 local_player->jy - MIDPOSY);
3767   }
3768
3769   /* do not use PLAYING mask for fading out from main screen */
3770   game_status = GAME_MODE_MAIN;
3771
3772   StopAnimation();
3773
3774   if (!game.restart_level)
3775     CloseDoor(DOOR_CLOSE_1);
3776
3777 #if 1
3778   if (level_editor_test_game)
3779     FadeSkipNextFadeIn();
3780   else
3781     FadeSetEnterScreen();
3782 #else
3783   if (level_editor_test_game)
3784     fading = fading_none;
3785   else
3786     fading = menu.destination;
3787 #endif
3788
3789 #if 1
3790   FadeOut(REDRAW_FIELD);
3791 #else
3792   if (do_fading)
3793     FadeOut(REDRAW_FIELD);
3794 #endif
3795
3796   game_status = GAME_MODE_PLAYING;
3797
3798   /* !!! FIX THIS (START) !!! */
3799   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3800   {
3801     InitGameEngine_EM();
3802
3803     /* blit playfield from scroll buffer to normal back buffer for fading in */
3804     BlitScreenToBitmap_EM(backbuffer);
3805   }
3806   else
3807   {
3808     DrawLevel();
3809     DrawAllPlayers();
3810
3811     /* after drawing the level, correct some elements */
3812     if (game.timegate_time_left == 0)
3813       CloseAllOpenTimegates();
3814
3815     /* blit playfield from scroll buffer to normal back buffer for fading in */
3816     if (setup.soft_scrolling)
3817       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
3818
3819     redraw_mask |= REDRAW_FROM_BACKBUFFER;
3820   }
3821   /* !!! FIX THIS (END) !!! */
3822
3823 #if 1
3824   FadeIn(REDRAW_FIELD);
3825 #else
3826   if (do_fading)
3827     FadeIn(REDRAW_FIELD);
3828
3829   BackToFront();
3830 #endif
3831
3832   if (!game.restart_level)
3833   {
3834     /* copy default game door content to main double buffer */
3835     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3836                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
3837   }
3838
3839   SetPanelBackground();
3840   SetDrawBackgroundMask(REDRAW_DOOR_1);
3841
3842   UpdateGameDoorValues();
3843   DrawGameDoorValues();
3844
3845   if (!game.restart_level)
3846   {
3847     UnmapGameButtons();
3848     UnmapTapeButtons();
3849     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3850     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3851     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3852     MapGameButtons();
3853     MapTapeButtons();
3854
3855     /* copy actual game door content to door double buffer for OpenDoor() */
3856     BlitBitmap(drawto, bitmap_db_door,
3857                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
3858
3859     OpenDoor(DOOR_OPEN_ALL);
3860
3861     PlaySound(SND_GAME_STARTING);
3862
3863     if (setup.sound_music)
3864       PlayLevelMusic();
3865
3866     KeyboardAutoRepeatOffUnlessAutoplay();
3867
3868     if (options.debug)
3869     {
3870       for (i = 0; i < MAX_PLAYERS; i++)
3871         printf("Player %d %sactive.\n",
3872                i + 1, (stored_player[i].active ? "" : "not "));
3873     }
3874   }
3875
3876 #if 1
3877   UnmapAllGadgets();
3878
3879   MapGameButtons();
3880   MapTapeButtons();
3881 #endif
3882
3883   game.restart_level = FALSE;
3884 }
3885
3886 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
3887 {
3888   /* this is used for non-R'n'D game engines to update certain engine values */
3889
3890   /* needed to determine if sounds are played within the visible screen area */
3891   scroll_x = actual_scroll_x;
3892   scroll_y = actual_scroll_y;
3893 }
3894
3895 void InitMovDir(int x, int y)
3896 {
3897   int i, element = Feld[x][y];
3898   static int xy[4][2] =
3899   {
3900     {  0, +1 },
3901     { +1,  0 },
3902     {  0, -1 },
3903     { -1,  0 }
3904   };
3905   static int direction[3][4] =
3906   {
3907     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
3908     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
3909     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
3910   };
3911
3912   switch (element)
3913   {
3914     case EL_BUG_RIGHT:
3915     case EL_BUG_UP:
3916     case EL_BUG_LEFT:
3917     case EL_BUG_DOWN:
3918       Feld[x][y] = EL_BUG;
3919       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
3920       break;
3921
3922     case EL_SPACESHIP_RIGHT:
3923     case EL_SPACESHIP_UP:
3924     case EL_SPACESHIP_LEFT:
3925     case EL_SPACESHIP_DOWN:
3926       Feld[x][y] = EL_SPACESHIP;
3927       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
3928       break;
3929
3930     case EL_BD_BUTTERFLY_RIGHT:
3931     case EL_BD_BUTTERFLY_UP:
3932     case EL_BD_BUTTERFLY_LEFT:
3933     case EL_BD_BUTTERFLY_DOWN:
3934       Feld[x][y] = EL_BD_BUTTERFLY;
3935       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
3936       break;
3937
3938     case EL_BD_FIREFLY_RIGHT:
3939     case EL_BD_FIREFLY_UP:
3940     case EL_BD_FIREFLY_LEFT:
3941     case EL_BD_FIREFLY_DOWN:
3942       Feld[x][y] = EL_BD_FIREFLY;
3943       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
3944       break;
3945
3946     case EL_PACMAN_RIGHT:
3947     case EL_PACMAN_UP:
3948     case EL_PACMAN_LEFT:
3949     case EL_PACMAN_DOWN:
3950       Feld[x][y] = EL_PACMAN;
3951       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
3952       break;
3953
3954     case EL_YAMYAM_LEFT:
3955     case EL_YAMYAM_RIGHT:
3956     case EL_YAMYAM_UP:
3957     case EL_YAMYAM_DOWN:
3958       Feld[x][y] = EL_YAMYAM;
3959       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
3960       break;
3961
3962     case EL_SP_SNIKSNAK:
3963       MovDir[x][y] = MV_UP;
3964       break;
3965
3966     case EL_SP_ELECTRON:
3967       MovDir[x][y] = MV_LEFT;
3968       break;
3969
3970     case EL_MOLE_LEFT:
3971     case EL_MOLE_RIGHT:
3972     case EL_MOLE_UP:
3973     case EL_MOLE_DOWN:
3974       Feld[x][y] = EL_MOLE;
3975       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
3976       break;
3977
3978     default:
3979       if (IS_CUSTOM_ELEMENT(element))
3980       {
3981         struct ElementInfo *ei = &element_info[element];
3982         int move_direction_initial = ei->move_direction_initial;
3983         int move_pattern = ei->move_pattern;
3984
3985         if (move_direction_initial == MV_START_PREVIOUS)
3986         {
3987           if (MovDir[x][y] != MV_NONE)
3988             return;
3989
3990           move_direction_initial = MV_START_AUTOMATIC;
3991         }
3992
3993         if (move_direction_initial == MV_START_RANDOM)
3994           MovDir[x][y] = 1 << RND(4);
3995         else if (move_direction_initial & MV_ANY_DIRECTION)
3996           MovDir[x][y] = move_direction_initial;
3997         else if (move_pattern == MV_ALL_DIRECTIONS ||
3998                  move_pattern == MV_TURNING_LEFT ||
3999                  move_pattern == MV_TURNING_RIGHT ||
4000                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4001                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4002                  move_pattern == MV_TURNING_RANDOM)
4003           MovDir[x][y] = 1 << RND(4);
4004         else if (move_pattern == MV_HORIZONTAL)
4005           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4006         else if (move_pattern == MV_VERTICAL)
4007           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4008         else if (move_pattern & MV_ANY_DIRECTION)
4009           MovDir[x][y] = element_info[element].move_pattern;
4010         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4011                  move_pattern == MV_ALONG_RIGHT_SIDE)
4012         {
4013           /* use random direction as default start direction */
4014           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4015             MovDir[x][y] = 1 << RND(4);
4016
4017           for (i = 0; i < NUM_DIRECTIONS; i++)
4018           {
4019             int x1 = x + xy[i][0];
4020             int y1 = y + xy[i][1];
4021
4022             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4023             {
4024               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4025                 MovDir[x][y] = direction[0][i];
4026               else
4027                 MovDir[x][y] = direction[1][i];
4028
4029               break;
4030             }
4031           }
4032         }                
4033       }
4034       else
4035       {
4036         MovDir[x][y] = 1 << RND(4);
4037
4038         if (element != EL_BUG &&
4039             element != EL_SPACESHIP &&
4040             element != EL_BD_BUTTERFLY &&
4041             element != EL_BD_FIREFLY)
4042           break;
4043
4044         for (i = 0; i < NUM_DIRECTIONS; i++)
4045         {
4046           int x1 = x + xy[i][0];
4047           int y1 = y + xy[i][1];
4048
4049           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4050           {
4051             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4052             {
4053               MovDir[x][y] = direction[0][i];
4054               break;
4055             }
4056             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4057                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4058             {
4059               MovDir[x][y] = direction[1][i];
4060               break;
4061             }
4062           }
4063         }
4064       }
4065       break;
4066   }
4067
4068   GfxDir[x][y] = MovDir[x][y];
4069 }
4070
4071 void InitAmoebaNr(int x, int y)
4072 {
4073   int i;
4074   int group_nr = AmoebeNachbarNr(x, y);
4075
4076   if (group_nr == 0)
4077   {
4078     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4079     {
4080       if (AmoebaCnt[i] == 0)
4081       {
4082         group_nr = i;
4083         break;
4084       }
4085     }
4086   }
4087
4088   AmoebaNr[x][y] = group_nr;
4089   AmoebaCnt[group_nr]++;
4090   AmoebaCnt2[group_nr]++;
4091 }
4092
4093 static void PlayerWins(struct PlayerInfo *player)
4094 {
4095   player->LevelSolved = TRUE;
4096   player->GameOver = TRUE;
4097
4098   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4099                          level.native_em_level->lev->score : player->score);
4100 }
4101
4102 void GameWon()
4103 {
4104   static int time, time_final;
4105   static int score, score_final;
4106   static int game_over_delay_1 = 0;
4107   static int game_over_delay_2 = 0;
4108   int game_over_delay_value_1 = 50;
4109   int game_over_delay_value_2 = 50;
4110
4111   if (!local_player->LevelSolved_GameWon)
4112   {
4113     int i;
4114
4115     /* do not start end game actions before the player stops moving (to exit) */
4116     if (local_player->MovPos)
4117       return;
4118
4119     local_player->LevelSolved_GameWon = TRUE;
4120     local_player->LevelSolved_SaveTape = tape.recording;
4121     local_player->LevelSolved_SaveScore = !tape.playing;
4122
4123     if (tape.auto_play)         /* tape might already be stopped here */
4124       tape.auto_play_level_solved = TRUE;
4125
4126 #if 1
4127     TapeStop();
4128 #endif
4129
4130     game_over_delay_1 = game_over_delay_value_1;
4131     game_over_delay_2 = game_over_delay_value_2;
4132
4133     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4134     score = score_final = local_player->score_final;
4135
4136     if (TimeLeft > 0)
4137     {
4138       time_final = 0;
4139       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4140     }
4141     else if (level.time == 0 && TimePlayed < 999)
4142     {
4143       time_final = 999;
4144       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4145     }
4146
4147     local_player->score_final = score_final;
4148
4149     if (level_editor_test_game)
4150     {
4151       time = time_final;
4152       score = score_final;
4153
4154 #if 1
4155       game_panel_controls[GAME_PANEL_TIME].value = time;
4156       game_panel_controls[GAME_PANEL_SCORE].value = score;
4157
4158       DisplayGameControlValues();
4159 #else
4160       DrawGameValue_Time(time);
4161       DrawGameValue_Score(score);
4162 #endif
4163     }
4164
4165     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4166     {
4167       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4168       {
4169         /* close exit door after last player */
4170         if ((AllPlayersGone &&
4171              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4172               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4173               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4174             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4175             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4176         {
4177           int element = Feld[ExitX][ExitY];
4178
4179 #if 0
4180           if (element == EL_EM_EXIT_OPEN ||
4181               element == EL_EM_STEEL_EXIT_OPEN)
4182           {
4183             Bang(ExitX, ExitY);
4184           }
4185           else
4186 #endif
4187           {
4188             Feld[ExitX][ExitY] =
4189               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4190                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4191                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4192                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4193                EL_EM_STEEL_EXIT_CLOSING);
4194
4195             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4196           }
4197         }
4198
4199         /* player disappears */
4200         DrawLevelField(ExitX, ExitY);
4201       }
4202
4203       for (i = 0; i < MAX_PLAYERS; i++)
4204       {
4205         struct PlayerInfo *player = &stored_player[i];
4206
4207         if (player->present)
4208         {
4209           RemovePlayer(player);
4210
4211           /* player disappears */
4212           DrawLevelField(player->jx, player->jy);
4213         }
4214       }
4215     }
4216
4217     PlaySound(SND_GAME_WINNING);
4218   }
4219
4220   if (game_over_delay_1 > 0)
4221   {
4222     game_over_delay_1--;
4223
4224     return;
4225   }
4226
4227   if (time != time_final)
4228   {
4229     int time_to_go = ABS(time_final - time);
4230     int time_count_dir = (time < time_final ? +1 : -1);
4231     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4232
4233     time  += time_count_steps * time_count_dir;
4234     score += time_count_steps * level.score[SC_TIME_BONUS];
4235
4236 #if 1
4237     game_panel_controls[GAME_PANEL_TIME].value = time;
4238     game_panel_controls[GAME_PANEL_SCORE].value = score;
4239
4240     DisplayGameControlValues();
4241 #else
4242     DrawGameValue_Time(time);
4243     DrawGameValue_Score(score);
4244 #endif
4245
4246     if (time == time_final)
4247       StopSound(SND_GAME_LEVELTIME_BONUS);
4248     else if (setup.sound_loops)
4249       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4250     else
4251       PlaySound(SND_GAME_LEVELTIME_BONUS);
4252
4253     return;
4254   }
4255
4256   local_player->LevelSolved_PanelOff = TRUE;
4257
4258   if (game_over_delay_2 > 0)
4259   {
4260     game_over_delay_2--;
4261
4262     return;
4263   }
4264
4265 #if 1
4266   GameEnd();
4267 #endif
4268 }
4269
4270 void GameEnd()
4271 {
4272   int hi_pos;
4273   boolean raise_level = FALSE;
4274
4275   local_player->LevelSolved_GameEnd = TRUE;
4276
4277   CloseDoor(DOOR_CLOSE_1);
4278
4279   if (local_player->LevelSolved_SaveTape)
4280   {
4281 #if 0
4282     TapeStop();
4283 #endif
4284
4285 #if 1
4286     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4287 #else
4288     SaveTape(tape.level_nr);            /* ask to save tape */
4289 #endif
4290   }
4291
4292   if (level_editor_test_game)
4293   {
4294     game_status = GAME_MODE_MAIN;
4295
4296 #if 1
4297     DrawAndFadeInMainMenu(REDRAW_FIELD);
4298 #else
4299     DrawMainMenu();
4300 #endif
4301
4302     return;
4303   }
4304
4305   if (!local_player->LevelSolved_SaveScore)
4306   {
4307 #if 1
4308     FadeOut(REDRAW_FIELD);
4309 #endif
4310
4311     game_status = GAME_MODE_MAIN;
4312
4313     DrawAndFadeInMainMenu(REDRAW_FIELD);
4314
4315     return;
4316   }
4317
4318   if (level_nr == leveldir_current->handicap_level)
4319   {
4320     leveldir_current->handicap_level++;
4321     SaveLevelSetup_SeriesInfo();
4322   }
4323
4324   if (level_nr < leveldir_current->last_level)
4325     raise_level = TRUE;                 /* advance to next level */
4326
4327   if ((hi_pos = NewHiScore()) >= 0) 
4328   {
4329     game_status = GAME_MODE_SCORES;
4330
4331     DrawHallOfFame(hi_pos);
4332
4333     if (raise_level)
4334     {
4335       level_nr++;
4336       TapeErase();
4337     }
4338   }
4339   else
4340   {
4341 #if 1
4342     FadeOut(REDRAW_FIELD);
4343 #endif
4344
4345     game_status = GAME_MODE_MAIN;
4346
4347     if (raise_level)
4348     {
4349       level_nr++;
4350       TapeErase();
4351     }
4352
4353     DrawAndFadeInMainMenu(REDRAW_FIELD);
4354   }
4355 }
4356
4357 int NewHiScore()
4358 {
4359   int k, l;
4360   int position = -1;
4361
4362   LoadScore(level_nr);
4363
4364   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4365       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4366     return -1;
4367
4368   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4369   {
4370     if (local_player->score_final > highscore[k].Score)
4371     {
4372       /* player has made it to the hall of fame */
4373
4374       if (k < MAX_SCORE_ENTRIES - 1)
4375       {
4376         int m = MAX_SCORE_ENTRIES - 1;
4377
4378 #ifdef ONE_PER_NAME
4379         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4380           if (strEqual(setup.player_name, highscore[l].Name))
4381             m = l;
4382         if (m == k)     /* player's new highscore overwrites his old one */
4383           goto put_into_list;
4384 #endif
4385
4386         for (l = m; l > k; l--)
4387         {
4388           strcpy(highscore[l].Name, highscore[l - 1].Name);
4389           highscore[l].Score = highscore[l - 1].Score;
4390         }
4391       }
4392
4393 #ifdef ONE_PER_NAME
4394       put_into_list:
4395 #endif
4396       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4397       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4398       highscore[k].Score = local_player->score_final; 
4399       position = k;
4400       break;
4401     }
4402
4403 #ifdef ONE_PER_NAME
4404     else if (!strncmp(setup.player_name, highscore[k].Name,
4405                       MAX_PLAYER_NAME_LEN))
4406       break;    /* player already there with a higher score */
4407 #endif
4408
4409   }
4410
4411   if (position >= 0) 
4412     SaveScore(level_nr);
4413
4414   return position;
4415 }
4416
4417 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4418 {
4419   int element = Feld[x][y];
4420   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4421   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4422   int horiz_move = (dx != 0);
4423   int sign = (horiz_move ? dx : dy);
4424   int step = sign * element_info[element].move_stepsize;
4425
4426   /* special values for move stepsize for spring and things on conveyor belt */
4427   if (horiz_move)
4428   {
4429     if (CAN_FALL(element) &&
4430         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4431       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4432     else if (element == EL_SPRING)
4433       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4434   }
4435
4436   return step;
4437 }
4438
4439 inline static int getElementMoveStepsize(int x, int y)
4440 {
4441   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4442 }
4443
4444 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4445 {
4446   if (player->GfxAction != action || player->GfxDir != dir)
4447   {
4448 #if 0
4449     printf("Player frame reset! (%d => %d, %d => %d)\n",
4450            player->GfxAction, action, player->GfxDir, dir);
4451 #endif
4452
4453     player->GfxAction = action;
4454     player->GfxDir = dir;
4455     player->Frame = 0;
4456     player->StepFrame = 0;
4457   }
4458 }
4459
4460 #if USE_GFX_RESET_GFX_ANIMATION
4461 static void ResetGfxFrame(int x, int y, boolean redraw)
4462 {
4463   int element = Feld[x][y];
4464   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4465   int last_gfx_frame = GfxFrame[x][y];
4466
4467   if (graphic_info[graphic].anim_global_sync)
4468     GfxFrame[x][y] = FrameCounter;
4469   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4470     GfxFrame[x][y] = CustomValue[x][y];
4471   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4472     GfxFrame[x][y] = element_info[element].collect_score;
4473   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4474     GfxFrame[x][y] = ChangeDelay[x][y];
4475
4476   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4477     DrawLevelGraphicAnimation(x, y, graphic);
4478 }
4479 #endif
4480
4481 static void ResetGfxAnimation(int x, int y)
4482 {
4483   GfxAction[x][y] = ACTION_DEFAULT;
4484   GfxDir[x][y] = MovDir[x][y];
4485   GfxFrame[x][y] = 0;
4486
4487 #if USE_GFX_RESET_GFX_ANIMATION
4488   ResetGfxFrame(x, y, FALSE);
4489 #endif
4490 }
4491
4492 static void ResetRandomAnimationValue(int x, int y)
4493 {
4494   GfxRandom[x][y] = INIT_GFX_RANDOM();
4495 }
4496
4497 void InitMovingField(int x, int y, int direction)
4498 {
4499   int element = Feld[x][y];
4500   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4501   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4502   int newx = x + dx;
4503   int newy = y + dy;
4504   boolean is_moving_before, is_moving_after;
4505 #if 0
4506   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4507 #endif
4508
4509   /* check if element was/is moving or being moved before/after mode change */
4510 #if 1
4511 #if 1
4512   is_moving_before = (WasJustMoving[x][y] != 0);
4513 #else
4514   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4515   is_moving_before = WasJustMoving[x][y];
4516 #endif
4517 #else
4518   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4519 #endif
4520   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4521
4522   /* reset animation only for moving elements which change direction of moving
4523      or which just started or stopped moving
4524      (else CEs with property "can move" / "not moving" are reset each frame) */
4525 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4526 #if 1
4527   if (is_moving_before != is_moving_after ||
4528       direction != MovDir[x][y])
4529     ResetGfxAnimation(x, y);
4530 #else
4531   if ((is_moving_before || is_moving_after) && !continues_moving)
4532     ResetGfxAnimation(x, y);
4533 #endif
4534 #else
4535   if (!continues_moving)
4536     ResetGfxAnimation(x, y);
4537 #endif
4538
4539   MovDir[x][y] = direction;
4540   GfxDir[x][y] = direction;
4541
4542 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4543   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4544                      direction == MV_DOWN && CAN_FALL(element) ?
4545                      ACTION_FALLING : ACTION_MOVING);
4546 #else
4547   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4548                      ACTION_FALLING : ACTION_MOVING);
4549 #endif
4550
4551   /* this is needed for CEs with property "can move" / "not moving" */
4552
4553   if (is_moving_after)
4554   {
4555     if (Feld[newx][newy] == EL_EMPTY)
4556       Feld[newx][newy] = EL_BLOCKED;
4557
4558     MovDir[newx][newy] = MovDir[x][y];
4559
4560 #if USE_NEW_CUSTOM_VALUE
4561     CustomValue[newx][newy] = CustomValue[x][y];
4562 #endif
4563
4564     GfxFrame[newx][newy] = GfxFrame[x][y];
4565     GfxRandom[newx][newy] = GfxRandom[x][y];
4566     GfxAction[newx][newy] = GfxAction[x][y];
4567     GfxDir[newx][newy] = GfxDir[x][y];
4568   }
4569 }
4570
4571 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4572 {
4573   int direction = MovDir[x][y];
4574   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4575   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4576
4577   *goes_to_x = newx;
4578   *goes_to_y = newy;
4579 }
4580
4581 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4582 {
4583   int oldx = x, oldy = y;
4584   int direction = MovDir[x][y];
4585
4586   if (direction == MV_LEFT)
4587     oldx++;
4588   else if (direction == MV_RIGHT)
4589     oldx--;
4590   else if (direction == MV_UP)
4591     oldy++;
4592   else if (direction == MV_DOWN)
4593     oldy--;
4594
4595   *comes_from_x = oldx;
4596   *comes_from_y = oldy;
4597 }
4598
4599 int MovingOrBlocked2Element(int x, int y)
4600 {
4601   int element = Feld[x][y];
4602
4603   if (element == EL_BLOCKED)
4604   {
4605     int oldx, oldy;
4606
4607     Blocked2Moving(x, y, &oldx, &oldy);
4608     return Feld[oldx][oldy];
4609   }
4610   else
4611     return element;
4612 }
4613
4614 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4615 {
4616   /* like MovingOrBlocked2Element(), but if element is moving
4617      and (x,y) is the field the moving element is just leaving,
4618      return EL_BLOCKED instead of the element value */
4619   int element = Feld[x][y];
4620
4621   if (IS_MOVING(x, y))
4622   {
4623     if (element == EL_BLOCKED)
4624     {
4625       int oldx, oldy;
4626
4627       Blocked2Moving(x, y, &oldx, &oldy);
4628       return Feld[oldx][oldy];
4629     }
4630     else
4631       return EL_BLOCKED;
4632   }
4633   else
4634     return element;
4635 }
4636
4637 static void RemoveField(int x, int y)
4638 {
4639   Feld[x][y] = EL_EMPTY;
4640
4641   MovPos[x][y] = 0;
4642   MovDir[x][y] = 0;
4643   MovDelay[x][y] = 0;
4644
4645 #if USE_NEW_CUSTOM_VALUE
4646   CustomValue[x][y] = 0;
4647 #endif
4648
4649   AmoebaNr[x][y] = 0;
4650   ChangeDelay[x][y] = 0;
4651   ChangePage[x][y] = -1;
4652   Pushed[x][y] = FALSE;
4653
4654 #if 0
4655   ExplodeField[x][y] = EX_TYPE_NONE;
4656 #endif
4657
4658   GfxElement[x][y] = EL_UNDEFINED;
4659   GfxAction[x][y] = ACTION_DEFAULT;
4660   GfxDir[x][y] = MV_NONE;
4661 }
4662
4663 void RemoveMovingField(int x, int y)
4664 {
4665   int oldx = x, oldy = y, newx = x, newy = y;
4666   int element = Feld[x][y];
4667   int next_element = EL_UNDEFINED;
4668
4669   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4670     return;
4671
4672   if (IS_MOVING(x, y))
4673   {
4674     Moving2Blocked(x, y, &newx, &newy);
4675
4676     if (Feld[newx][newy] != EL_BLOCKED)
4677     {
4678       /* element is moving, but target field is not free (blocked), but
4679          already occupied by something different (example: acid pool);
4680          in this case, only remove the moving field, but not the target */
4681
4682       RemoveField(oldx, oldy);
4683
4684       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4685
4686       DrawLevelField(oldx, oldy);
4687
4688       return;
4689     }
4690   }
4691   else if (element == EL_BLOCKED)
4692   {
4693     Blocked2Moving(x, y, &oldx, &oldy);
4694     if (!IS_MOVING(oldx, oldy))
4695       return;
4696   }
4697
4698   if (element == EL_BLOCKED &&
4699       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4700        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4701        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4702        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4703        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4704        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4705     next_element = get_next_element(Feld[oldx][oldy]);
4706
4707   RemoveField(oldx, oldy);
4708   RemoveField(newx, newy);
4709
4710   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4711
4712   if (next_element != EL_UNDEFINED)
4713     Feld[oldx][oldy] = next_element;
4714
4715   DrawLevelField(oldx, oldy);
4716   DrawLevelField(newx, newy);
4717 }
4718
4719 void DrawDynamite(int x, int y)
4720 {
4721   int sx = SCREENX(x), sy = SCREENY(y);
4722   int graphic = el2img(Feld[x][y]);
4723   int frame;
4724
4725   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4726     return;
4727
4728   if (IS_WALKABLE_INSIDE(Back[x][y]))
4729     return;
4730
4731   if (Back[x][y])
4732     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4733   else if (Store[x][y])
4734     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4735
4736   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4737
4738   if (Back[x][y] || Store[x][y])
4739     DrawGraphicThruMask(sx, sy, graphic, frame);
4740   else
4741     DrawGraphic(sx, sy, graphic, frame);
4742 }
4743
4744 void CheckDynamite(int x, int y)
4745 {
4746   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4747   {
4748     MovDelay[x][y]--;
4749
4750     if (MovDelay[x][y] != 0)
4751     {
4752       DrawDynamite(x, y);
4753       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4754
4755       return;
4756     }
4757   }
4758
4759   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4760
4761   Bang(x, y);
4762 }
4763
4764 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4765 {
4766   boolean num_checked_players = 0;
4767   int i;
4768
4769   for (i = 0; i < MAX_PLAYERS; i++)
4770   {
4771     if (stored_player[i].active)
4772     {
4773       int sx = stored_player[i].jx;
4774       int sy = stored_player[i].jy;
4775
4776       if (num_checked_players == 0)
4777       {
4778         *sx1 = *sx2 = sx;
4779         *sy1 = *sy2 = sy;
4780       }
4781       else
4782       {
4783         *sx1 = MIN(*sx1, sx);
4784         *sy1 = MIN(*sy1, sy);
4785         *sx2 = MAX(*sx2, sx);
4786         *sy2 = MAX(*sy2, sy);
4787       }
4788
4789       num_checked_players++;
4790     }
4791   }
4792 }
4793
4794 static boolean checkIfAllPlayersFitToScreen_RND()
4795 {
4796   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4797
4798   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4799
4800   return (sx2 - sx1 < SCR_FIELDX &&
4801           sy2 - sy1 < SCR_FIELDY);
4802 }
4803
4804 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4805 {
4806   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4807
4808   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4809
4810   *sx = (sx1 + sx2) / 2;
4811   *sy = (sy1 + sy2) / 2;
4812 }
4813
4814 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4815                         boolean center_screen, boolean quick_relocation)
4816 {
4817   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4818   boolean no_delay = (tape.warp_forward);
4819   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4820   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4821
4822   if (quick_relocation)
4823   {
4824     int offset = game.scroll_delay_value;
4825
4826     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4827     {
4828       if (!level.shifted_relocation || center_screen)
4829       {
4830         /* quick relocation (without scrolling), with centering of screen */
4831
4832         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4833                     x > SBX_Right + MIDPOSX ? SBX_Right :
4834                     x - MIDPOSX);
4835
4836         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4837                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4838                     y - MIDPOSY);
4839       }
4840       else
4841       {
4842         /* quick relocation (without scrolling), but do not center screen */
4843
4844         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4845                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4846                                old_x - MIDPOSX);
4847
4848         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4849                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4850                                old_y - MIDPOSY);
4851
4852         int offset_x = x + (scroll_x - center_scroll_x);
4853         int offset_y = y + (scroll_y - center_scroll_y);
4854
4855         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4856                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4857                     offset_x - MIDPOSX);
4858
4859         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4860                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4861                     offset_y - MIDPOSY);
4862       }
4863     }
4864     else
4865     {
4866       /* quick relocation (without scrolling), inside visible screen area */
4867
4868       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
4869           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
4870         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
4871
4872       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
4873           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
4874         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
4875
4876       /* don't scroll over playfield boundaries */
4877       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
4878         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
4879
4880       /* don't scroll over playfield boundaries */
4881       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
4882         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
4883     }
4884
4885     RedrawPlayfield(TRUE, 0,0,0,0);
4886   }
4887   else
4888   {
4889 #if 1
4890     int scroll_xx, scroll_yy;
4891
4892     if (!level.shifted_relocation || center_screen)
4893     {
4894       /* visible relocation (with scrolling), with centering of screen */
4895
4896       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4897                    x > SBX_Right + MIDPOSX ? SBX_Right :
4898                    x - MIDPOSX);
4899
4900       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4901                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4902                    y - MIDPOSY);
4903     }
4904     else
4905     {
4906       /* visible relocation (with scrolling), but do not center screen */
4907
4908       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4909                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4910                              old_x - MIDPOSX);
4911
4912       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4913                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4914                              old_y - MIDPOSY);
4915
4916       int offset_x = x + (scroll_x - center_scroll_x);
4917       int offset_y = y + (scroll_y - center_scroll_y);
4918
4919       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4920                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4921                    offset_x - MIDPOSX);
4922
4923       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4924                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4925                    offset_y - MIDPOSY);
4926     }
4927
4928 #else
4929
4930     /* visible relocation (with scrolling), with centering of screen */
4931
4932     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4933                      x > SBX_Right + MIDPOSX ? SBX_Right :
4934                      x - MIDPOSX);
4935
4936     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4937                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
4938                      y - MIDPOSY);
4939 #endif
4940
4941     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
4942
4943     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
4944     {
4945       int dx = 0, dy = 0;
4946       int fx = FX, fy = FY;
4947
4948       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
4949       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
4950
4951       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
4952         break;
4953
4954       scroll_x -= dx;
4955       scroll_y -= dy;
4956
4957       fx += dx * TILEX / 2;
4958       fy += dy * TILEY / 2;
4959
4960       ScrollLevel(dx, dy);
4961       DrawAllPlayers();
4962
4963       /* scroll in two steps of half tile size to make things smoother */
4964       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4965       FlushDisplay();
4966       Delay(wait_delay_value);
4967
4968       /* scroll second step to align at full tile size */
4969       BackToFront();
4970       Delay(wait_delay_value);
4971     }
4972
4973     DrawAllPlayers();
4974     BackToFront();
4975     Delay(wait_delay_value);
4976   }
4977 }
4978
4979 void RelocatePlayer(int jx, int jy, int el_player_raw)
4980 {
4981   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4982   int player_nr = GET_PLAYER_NR(el_player);
4983   struct PlayerInfo *player = &stored_player[player_nr];
4984   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4985   boolean no_delay = (tape.warp_forward);
4986   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4987   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4988   int old_jx = player->jx;
4989   int old_jy = player->jy;
4990   int old_element = Feld[old_jx][old_jy];
4991   int element = Feld[jx][jy];
4992   boolean player_relocated = (old_jx != jx || old_jy != jy);
4993
4994   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
4995   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
4996   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
4997   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
4998   int leave_side_horiz = move_dir_horiz;
4999   int leave_side_vert  = move_dir_vert;
5000   int enter_side = enter_side_horiz | enter_side_vert;
5001   int leave_side = leave_side_horiz | leave_side_vert;
5002
5003   if (player->GameOver)         /* do not reanimate dead player */
5004     return;
5005
5006   if (!player_relocated)        /* no need to relocate the player */
5007     return;
5008
5009   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5010   {
5011     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5012     DrawLevelField(jx, jy);
5013   }
5014
5015   if (player->present)
5016   {
5017     while (player->MovPos)
5018     {
5019       ScrollPlayer(player, SCROLL_GO_ON);
5020       ScrollScreen(NULL, SCROLL_GO_ON);
5021
5022       AdvanceFrameAndPlayerCounters(player->index_nr);
5023
5024       DrawPlayer(player);
5025
5026       BackToFront();
5027       Delay(wait_delay_value);
5028     }
5029
5030     DrawPlayer(player);         /* needed here only to cleanup last field */
5031     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5032
5033     player->is_moving = FALSE;
5034   }
5035
5036   if (IS_CUSTOM_ELEMENT(old_element))
5037     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5038                                CE_LEFT_BY_PLAYER,
5039                                player->index_bit, leave_side);
5040
5041   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5042                                       CE_PLAYER_LEAVES_X,
5043                                       player->index_bit, leave_side);
5044
5045   Feld[jx][jy] = el_player;
5046   InitPlayerField(jx, jy, el_player, TRUE);
5047
5048   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5049   {
5050     Feld[jx][jy] = element;
5051     InitField(jx, jy, FALSE);
5052   }
5053
5054   /* only visually relocate centered player */
5055   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5056                      FALSE, level.instant_relocation);
5057
5058   TestIfPlayerTouchesBadThing(jx, jy);
5059   TestIfPlayerTouchesCustomElement(jx, jy);
5060
5061   if (IS_CUSTOM_ELEMENT(element))
5062     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5063                                player->index_bit, enter_side);
5064
5065   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5066                                       player->index_bit, enter_side);
5067 }
5068
5069 void Explode(int ex, int ey, int phase, int mode)
5070 {
5071   int x, y;
5072   int last_phase;
5073   int border_element;
5074
5075   /* !!! eliminate this variable !!! */
5076   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5077
5078   if (game.explosions_delayed)
5079   {
5080     ExplodeField[ex][ey] = mode;
5081     return;
5082   }
5083
5084   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5085   {
5086     int center_element = Feld[ex][ey];
5087     int artwork_element, explosion_element;     /* set these values later */
5088
5089 #if 0
5090     /* --- This is only really needed (and now handled) in "Impact()". --- */
5091     /* do not explode moving elements that left the explode field in time */
5092     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5093         center_element == EL_EMPTY &&
5094         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5095       return;
5096 #endif
5097
5098 #if 0
5099     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5100     if (mode == EX_TYPE_NORMAL ||
5101         mode == EX_TYPE_CENTER ||
5102         mode == EX_TYPE_CROSS)
5103       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5104 #endif
5105
5106     /* remove things displayed in background while burning dynamite */
5107     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5108       Back[ex][ey] = 0;
5109
5110     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5111     {
5112       /* put moving element to center field (and let it explode there) */
5113       center_element = MovingOrBlocked2Element(ex, ey);
5114       RemoveMovingField(ex, ey);
5115       Feld[ex][ey] = center_element;
5116     }
5117
5118     /* now "center_element" is finally determined -- set related values now */
5119     artwork_element = center_element;           /* for custom player artwork */
5120     explosion_element = center_element;         /* for custom player artwork */
5121
5122     if (IS_PLAYER(ex, ey))
5123     {
5124       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5125
5126       artwork_element = stored_player[player_nr].artwork_element;
5127
5128       if (level.use_explosion_element[player_nr])
5129       {
5130         explosion_element = level.explosion_element[player_nr];
5131         artwork_element = explosion_element;
5132       }
5133     }
5134
5135 #if 1
5136     if (mode == EX_TYPE_NORMAL ||
5137         mode == EX_TYPE_CENTER ||
5138         mode == EX_TYPE_CROSS)
5139       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5140 #endif
5141
5142     last_phase = element_info[explosion_element].explosion_delay + 1;
5143
5144     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5145     {
5146       int xx = x - ex + 1;
5147       int yy = y - ey + 1;
5148       int element;
5149
5150       if (!IN_LEV_FIELD(x, y) ||
5151           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5152           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5153         continue;
5154
5155       element = Feld[x][y];
5156
5157       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5158       {
5159         element = MovingOrBlocked2Element(x, y);
5160
5161         if (!IS_EXPLOSION_PROOF(element))
5162           RemoveMovingField(x, y);
5163       }
5164
5165       /* indestructible elements can only explode in center (but not flames) */
5166       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5167                                            mode == EX_TYPE_BORDER)) ||
5168           element == EL_FLAMES)
5169         continue;
5170
5171       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5172          behaviour, for example when touching a yamyam that explodes to rocks
5173          with active deadly shield, a rock is created under the player !!! */
5174       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5175 #if 0
5176       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5177           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5178            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5179 #else
5180       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5181 #endif
5182       {
5183         if (IS_ACTIVE_BOMB(element))
5184         {
5185           /* re-activate things under the bomb like gate or penguin */
5186           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5187           Back[x][y] = 0;
5188         }
5189
5190         continue;
5191       }
5192
5193       /* save walkable background elements while explosion on same tile */
5194       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5195           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5196         Back[x][y] = element;
5197
5198       /* ignite explodable elements reached by other explosion */
5199       if (element == EL_EXPLOSION)
5200         element = Store2[x][y];
5201
5202       if (AmoebaNr[x][y] &&
5203           (element == EL_AMOEBA_FULL ||
5204            element == EL_BD_AMOEBA ||
5205            element == EL_AMOEBA_GROWING))
5206       {
5207         AmoebaCnt[AmoebaNr[x][y]]--;
5208         AmoebaCnt2[AmoebaNr[x][y]]--;
5209       }
5210
5211       RemoveField(x, y);
5212
5213       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5214       {
5215         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5216
5217         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5218
5219         if (PLAYERINFO(ex, ey)->use_murphy)
5220           Store[x][y] = EL_EMPTY;
5221       }
5222
5223       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5224          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5225       else if (ELEM_IS_PLAYER(center_element))
5226         Store[x][y] = EL_EMPTY;
5227       else if (center_element == EL_YAMYAM)
5228         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5229       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5230         Store[x][y] = element_info[center_element].content.e[xx][yy];
5231 #if 1
5232       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5233          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5234          otherwise) -- FIX THIS !!! */
5235       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5236         Store[x][y] = element_info[element].content.e[1][1];
5237 #else
5238       else if (!CAN_EXPLODE(element))
5239         Store[x][y] = element_info[element].content.e[1][1];
5240 #endif
5241       else
5242         Store[x][y] = EL_EMPTY;
5243
5244       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5245           center_element == EL_AMOEBA_TO_DIAMOND)
5246         Store2[x][y] = element;
5247
5248       Feld[x][y] = EL_EXPLOSION;
5249       GfxElement[x][y] = artwork_element;
5250
5251       ExplodePhase[x][y] = 1;
5252       ExplodeDelay[x][y] = last_phase;
5253
5254       Stop[x][y] = TRUE;
5255     }
5256
5257     if (center_element == EL_YAMYAM)
5258       game.yamyam_content_nr =
5259         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5260
5261     return;
5262   }
5263
5264   if (Stop[ex][ey])
5265     return;
5266
5267   x = ex;
5268   y = ey;
5269
5270   if (phase == 1)
5271     GfxFrame[x][y] = 0;         /* restart explosion animation */
5272
5273   last_phase = ExplodeDelay[x][y];
5274
5275   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5276
5277 #ifdef DEBUG
5278
5279   /* activate this even in non-DEBUG version until cause for crash in
5280      getGraphicAnimationFrame() (see below) is found and eliminated */
5281
5282 #endif
5283 #if 1
5284
5285 #if 1
5286   /* this can happen if the player leaves an explosion just in time */
5287   if (GfxElement[x][y] == EL_UNDEFINED)
5288     GfxElement[x][y] = EL_EMPTY;
5289 #else
5290   if (GfxElement[x][y] == EL_UNDEFINED)
5291   {
5292     printf("\n\n");
5293     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5294     printf("Explode(): This should never happen!\n");
5295     printf("\n\n");
5296
5297     GfxElement[x][y] = EL_EMPTY;
5298   }
5299 #endif
5300
5301 #endif
5302
5303   border_element = Store2[x][y];
5304   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5305     border_element = StorePlayer[x][y];
5306
5307   if (phase == element_info[border_element].ignition_delay ||
5308       phase == last_phase)
5309   {
5310     boolean border_explosion = FALSE;
5311
5312     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5313         !PLAYER_EXPLOSION_PROTECTED(x, y))
5314     {
5315       KillPlayerUnlessExplosionProtected(x, y);
5316       border_explosion = TRUE;
5317     }
5318     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5319     {
5320       Feld[x][y] = Store2[x][y];
5321       Store2[x][y] = 0;
5322       Bang(x, y);
5323       border_explosion = TRUE;
5324     }
5325     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5326     {
5327       AmoebeUmwandeln(x, y);
5328       Store2[x][y] = 0;
5329       border_explosion = TRUE;
5330     }
5331
5332     /* if an element just explodes due to another explosion (chain-reaction),
5333        do not immediately end the new explosion when it was the last frame of
5334        the explosion (as it would be done in the following "if"-statement!) */
5335     if (border_explosion && phase == last_phase)
5336       return;
5337   }
5338
5339   if (phase == last_phase)
5340   {
5341     int element;
5342
5343     element = Feld[x][y] = Store[x][y];
5344     Store[x][y] = Store2[x][y] = 0;
5345     GfxElement[x][y] = EL_UNDEFINED;
5346
5347     /* player can escape from explosions and might therefore be still alive */
5348     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5349         element <= EL_PLAYER_IS_EXPLODING_4)
5350     {
5351       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5352       int explosion_element = EL_PLAYER_1 + player_nr;
5353       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5354       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5355
5356       if (level.use_explosion_element[player_nr])
5357         explosion_element = level.explosion_element[player_nr];
5358
5359       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5360                     element_info[explosion_element].content.e[xx][yy]);
5361     }
5362
5363     /* restore probably existing indestructible background element */
5364     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5365       element = Feld[x][y] = Back[x][y];
5366     Back[x][y] = 0;
5367
5368     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5369     GfxDir[x][y] = MV_NONE;
5370     ChangeDelay[x][y] = 0;
5371     ChangePage[x][y] = -1;
5372
5373 #if USE_NEW_CUSTOM_VALUE
5374     CustomValue[x][y] = 0;
5375 #endif
5376
5377     InitField_WithBug2(x, y, FALSE);
5378
5379     DrawLevelField(x, y);
5380
5381     TestIfElementTouchesCustomElement(x, y);
5382
5383     if (GFX_CRUMBLED(element))
5384       DrawLevelFieldCrumbledSandNeighbours(x, y);
5385
5386     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5387       StorePlayer[x][y] = 0;
5388
5389     if (ELEM_IS_PLAYER(element))
5390       RelocatePlayer(x, y, element);
5391   }
5392   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5393   {
5394     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5395     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5396
5397     if (phase == delay)
5398       DrawLevelFieldCrumbledSand(x, y);
5399
5400     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5401     {
5402       DrawLevelElement(x, y, Back[x][y]);
5403       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5404     }
5405     else if (IS_WALKABLE_UNDER(Back[x][y]))
5406     {
5407       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5408       DrawLevelElementThruMask(x, y, Back[x][y]);
5409     }
5410     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5411       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5412   }
5413 }
5414
5415 void DynaExplode(int ex, int ey)
5416 {
5417   int i, j;
5418   int dynabomb_element = Feld[ex][ey];
5419   int dynabomb_size = 1;
5420   boolean dynabomb_xl = FALSE;
5421   struct PlayerInfo *player;
5422   static int xy[4][2] =
5423   {
5424     { 0, -1 },
5425     { -1, 0 },
5426     { +1, 0 },
5427     { 0, +1 }
5428   };
5429
5430   if (IS_ACTIVE_BOMB(dynabomb_element))
5431   {
5432     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5433     dynabomb_size = player->dynabomb_size;
5434     dynabomb_xl = player->dynabomb_xl;
5435     player->dynabombs_left++;
5436   }
5437
5438   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5439
5440   for (i = 0; i < NUM_DIRECTIONS; i++)
5441   {
5442     for (j = 1; j <= dynabomb_size; j++)
5443     {
5444       int x = ex + j * xy[i][0];
5445       int y = ey + j * xy[i][1];
5446       int element;
5447
5448       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5449         break;
5450
5451       element = Feld[x][y];
5452
5453       /* do not restart explosions of fields with active bombs */
5454       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5455         continue;
5456
5457       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5458
5459       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5460           !IS_DIGGABLE(element) && !dynabomb_xl)
5461         break;
5462     }
5463   }
5464 }
5465
5466 void Bang(int x, int y)
5467 {
5468   int element = MovingOrBlocked2Element(x, y);
5469   int explosion_type = EX_TYPE_NORMAL;
5470
5471   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5472   {
5473     struct PlayerInfo *player = PLAYERINFO(x, y);
5474
5475     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5476                             player->element_nr);
5477
5478     if (level.use_explosion_element[player->index_nr])
5479     {
5480       int explosion_element = level.explosion_element[player->index_nr];
5481
5482       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5483         explosion_type = EX_TYPE_CROSS;
5484       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5485         explosion_type = EX_TYPE_CENTER;
5486     }
5487   }
5488
5489   switch (element)
5490   {
5491     case EL_BUG:
5492     case EL_SPACESHIP:
5493     case EL_BD_BUTTERFLY:
5494     case EL_BD_FIREFLY:
5495     case EL_YAMYAM:
5496     case EL_DARK_YAMYAM:
5497     case EL_ROBOT:
5498     case EL_PACMAN:
5499     case EL_MOLE:
5500       RaiseScoreElement(element);
5501       break;
5502
5503     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5504     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5505     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5506     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5507     case EL_DYNABOMB_INCREASE_NUMBER:
5508     case EL_DYNABOMB_INCREASE_SIZE:
5509     case EL_DYNABOMB_INCREASE_POWER:
5510       explosion_type = EX_TYPE_DYNA;
5511       break;
5512
5513     case EL_DC_LANDMINE:
5514 #if 0
5515     case EL_EM_EXIT_OPEN:
5516     case EL_EM_STEEL_EXIT_OPEN:
5517 #endif
5518       explosion_type = EX_TYPE_CENTER;
5519       break;
5520
5521     case EL_PENGUIN:
5522     case EL_LAMP:
5523     case EL_LAMP_ACTIVE:
5524     case EL_AMOEBA_TO_DIAMOND:
5525       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5526         explosion_type = EX_TYPE_CENTER;
5527       break;
5528
5529     default:
5530       if (element_info[element].explosion_type == EXPLODES_CROSS)
5531         explosion_type = EX_TYPE_CROSS;
5532       else if (element_info[element].explosion_type == EXPLODES_1X1)
5533         explosion_type = EX_TYPE_CENTER;
5534       break;
5535   }
5536
5537   if (explosion_type == EX_TYPE_DYNA)
5538     DynaExplode(x, y);
5539   else
5540     Explode(x, y, EX_PHASE_START, explosion_type);
5541
5542   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5543 }
5544
5545 void SplashAcid(int x, int y)
5546 {
5547   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5548       (!IN_LEV_FIELD(x - 1, y - 2) ||
5549        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5550     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5551
5552   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5553       (!IN_LEV_FIELD(x + 1, y - 2) ||
5554        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5555     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5556
5557   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5558 }
5559
5560 static void InitBeltMovement()
5561 {
5562   static int belt_base_element[4] =
5563   {
5564     EL_CONVEYOR_BELT_1_LEFT,
5565     EL_CONVEYOR_BELT_2_LEFT,
5566     EL_CONVEYOR_BELT_3_LEFT,
5567     EL_CONVEYOR_BELT_4_LEFT
5568   };
5569   static int belt_base_active_element[4] =
5570   {
5571     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5572     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5573     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5574     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5575   };
5576
5577   int x, y, i, j;
5578
5579   /* set frame order for belt animation graphic according to belt direction */
5580   for (i = 0; i < NUM_BELTS; i++)
5581   {
5582     int belt_nr = i;
5583
5584     for (j = 0; j < NUM_BELT_PARTS; j++)
5585     {
5586       int element = belt_base_active_element[belt_nr] + j;
5587       int graphic_1 = el2img(element);
5588       int graphic_2 = el2panelimg(element);
5589
5590       if (game.belt_dir[i] == MV_LEFT)
5591       {
5592         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5593         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5594       }
5595       else
5596       {
5597         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5598         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5599       }
5600     }
5601   }
5602
5603   SCAN_PLAYFIELD(x, y)
5604   {
5605     int element = Feld[x][y];
5606
5607     for (i = 0; i < NUM_BELTS; i++)
5608     {
5609       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5610       {
5611         int e_belt_nr = getBeltNrFromBeltElement(element);
5612         int belt_nr = i;
5613
5614         if (e_belt_nr == belt_nr)
5615         {
5616           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5617
5618           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5619         }
5620       }
5621     }
5622   }
5623 }
5624
5625 static void ToggleBeltSwitch(int x, int y)
5626 {
5627   static int belt_base_element[4] =
5628   {
5629     EL_CONVEYOR_BELT_1_LEFT,
5630     EL_CONVEYOR_BELT_2_LEFT,
5631     EL_CONVEYOR_BELT_3_LEFT,
5632     EL_CONVEYOR_BELT_4_LEFT
5633   };
5634   static int belt_base_active_element[4] =
5635   {
5636     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5637     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5638     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5639     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5640   };
5641   static int belt_base_switch_element[4] =
5642   {
5643     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5644     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5645     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5646     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5647   };
5648   static int belt_move_dir[4] =
5649   {
5650     MV_LEFT,
5651     MV_NONE,
5652     MV_RIGHT,
5653     MV_NONE,
5654   };
5655
5656   int element = Feld[x][y];
5657   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5658   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5659   int belt_dir = belt_move_dir[belt_dir_nr];
5660   int xx, yy, i;
5661
5662   if (!IS_BELT_SWITCH(element))
5663     return;
5664
5665   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5666   game.belt_dir[belt_nr] = belt_dir;
5667
5668   if (belt_dir_nr == 3)
5669     belt_dir_nr = 1;
5670
5671   /* set frame order for belt animation graphic according to belt direction */
5672   for (i = 0; i < NUM_BELT_PARTS; i++)
5673   {
5674     int element = belt_base_active_element[belt_nr] + i;
5675     int graphic_1 = el2img(element);
5676     int graphic_2 = el2panelimg(element);
5677
5678     if (belt_dir == MV_LEFT)
5679     {
5680       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5681       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5682     }
5683     else
5684     {
5685       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5686       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5687     }
5688   }
5689
5690   SCAN_PLAYFIELD(xx, yy)
5691   {
5692     int element = Feld[xx][yy];
5693
5694     if (IS_BELT_SWITCH(element))
5695     {
5696       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5697
5698       if (e_belt_nr == belt_nr)
5699       {
5700         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5701         DrawLevelField(xx, yy);
5702       }
5703     }
5704     else if (IS_BELT(element) && belt_dir != MV_NONE)
5705     {
5706       int e_belt_nr = getBeltNrFromBeltElement(element);
5707
5708       if (e_belt_nr == belt_nr)
5709       {
5710         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5711
5712         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5713         DrawLevelField(xx, yy);
5714       }
5715     }
5716     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5717     {
5718       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5719
5720       if (e_belt_nr == belt_nr)
5721       {
5722         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5723
5724         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5725         DrawLevelField(xx, yy);
5726       }
5727     }
5728   }
5729 }
5730
5731 static void ToggleSwitchgateSwitch(int x, int y)
5732 {
5733   int xx, yy;
5734
5735   game.switchgate_pos = !game.switchgate_pos;
5736
5737   SCAN_PLAYFIELD(xx, yy)
5738   {
5739     int element = Feld[xx][yy];
5740
5741 #if !USE_BOTH_SWITCHGATE_SWITCHES
5742     if (element == EL_SWITCHGATE_SWITCH_UP ||
5743         element == EL_SWITCHGATE_SWITCH_DOWN)
5744     {
5745       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5746       DrawLevelField(xx, yy);
5747     }
5748     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
5749              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5750     {
5751       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
5752       DrawLevelField(xx, yy);
5753     }
5754 #else
5755     if (element == EL_SWITCHGATE_SWITCH_UP)
5756     {
5757       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5758       DrawLevelField(xx, yy);
5759     }
5760     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5761     {
5762       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5763       DrawLevelField(xx, yy);
5764     }
5765     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5766     {
5767       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5768       DrawLevelField(xx, yy);
5769     }
5770     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5771     {
5772       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5773       DrawLevelField(xx, yy);
5774     }
5775 #endif
5776     else if (element == EL_SWITCHGATE_OPEN ||
5777              element == EL_SWITCHGATE_OPENING)
5778     {
5779       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5780
5781       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5782     }
5783     else if (element == EL_SWITCHGATE_CLOSED ||
5784              element == EL_SWITCHGATE_CLOSING)
5785     {
5786       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5787
5788       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5789     }
5790   }
5791 }
5792
5793 static int getInvisibleActiveFromInvisibleElement(int element)
5794 {
5795   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5796           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5797           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5798           element);
5799 }
5800
5801 static int getInvisibleFromInvisibleActiveElement(int element)
5802 {
5803   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5804           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5805           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5806           element);
5807 }
5808
5809 static void RedrawAllLightSwitchesAndInvisibleElements()
5810 {
5811   int x, y;
5812
5813   SCAN_PLAYFIELD(x, y)
5814   {
5815     int element = Feld[x][y];
5816
5817     if (element == EL_LIGHT_SWITCH &&
5818         game.light_time_left > 0)
5819     {
5820       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5821       DrawLevelField(x, y);
5822     }
5823     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5824              game.light_time_left == 0)
5825     {
5826       Feld[x][y] = EL_LIGHT_SWITCH;
5827       DrawLevelField(x, y);
5828     }
5829     else if (element == EL_EMC_DRIPPER &&
5830              game.light_time_left > 0)
5831     {
5832       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5833       DrawLevelField(x, y);
5834     }
5835     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5836              game.light_time_left == 0)
5837     {
5838       Feld[x][y] = EL_EMC_DRIPPER;
5839       DrawLevelField(x, y);
5840     }
5841     else if (element == EL_INVISIBLE_STEELWALL ||
5842              element == EL_INVISIBLE_WALL ||
5843              element == EL_INVISIBLE_SAND)
5844     {
5845       if (game.light_time_left > 0)
5846         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5847
5848       DrawLevelField(x, y);
5849
5850       /* uncrumble neighbour fields, if needed */
5851       if (element == EL_INVISIBLE_SAND)
5852         DrawLevelFieldCrumbledSandNeighbours(x, y);
5853     }
5854     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5855              element == EL_INVISIBLE_WALL_ACTIVE ||
5856              element == EL_INVISIBLE_SAND_ACTIVE)
5857     {
5858       if (game.light_time_left == 0)
5859         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5860
5861       DrawLevelField(x, y);
5862
5863       /* re-crumble neighbour fields, if needed */
5864       if (element == EL_INVISIBLE_SAND)
5865         DrawLevelFieldCrumbledSandNeighbours(x, y);
5866     }
5867   }
5868 }
5869
5870 static void RedrawAllInvisibleElementsForLenses()
5871 {
5872   int x, y;
5873
5874   SCAN_PLAYFIELD(x, y)
5875   {
5876     int element = Feld[x][y];
5877
5878     if (element == EL_EMC_DRIPPER &&
5879         game.lenses_time_left > 0)
5880     {
5881       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5882       DrawLevelField(x, y);
5883     }
5884     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5885              game.lenses_time_left == 0)
5886     {
5887       Feld[x][y] = EL_EMC_DRIPPER;
5888       DrawLevelField(x, y);
5889     }
5890     else if (element == EL_INVISIBLE_STEELWALL ||
5891              element == EL_INVISIBLE_WALL ||
5892              element == EL_INVISIBLE_SAND)
5893     {
5894       if (game.lenses_time_left > 0)
5895         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5896
5897       DrawLevelField(x, y);
5898
5899       /* uncrumble neighbour fields, if needed */
5900       if (element == EL_INVISIBLE_SAND)
5901         DrawLevelFieldCrumbledSandNeighbours(x, y);
5902     }
5903     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5904              element == EL_INVISIBLE_WALL_ACTIVE ||
5905              element == EL_INVISIBLE_SAND_ACTIVE)
5906     {
5907       if (game.lenses_time_left == 0)
5908         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5909
5910       DrawLevelField(x, y);
5911
5912       /* re-crumble neighbour fields, if needed */
5913       if (element == EL_INVISIBLE_SAND)
5914         DrawLevelFieldCrumbledSandNeighbours(x, y);
5915     }
5916   }
5917 }
5918
5919 static void RedrawAllInvisibleElementsForMagnifier()
5920 {
5921   int x, y;
5922
5923   SCAN_PLAYFIELD(x, y)
5924   {
5925     int element = Feld[x][y];
5926
5927     if (element == EL_EMC_FAKE_GRASS &&
5928         game.magnify_time_left > 0)
5929     {
5930       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5931       DrawLevelField(x, y);
5932     }
5933     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5934              game.magnify_time_left == 0)
5935     {
5936       Feld[x][y] = EL_EMC_FAKE_GRASS;
5937       DrawLevelField(x, y);
5938     }
5939     else if (IS_GATE_GRAY(element) &&
5940              game.magnify_time_left > 0)
5941     {
5942       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5943                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5944                     IS_EM_GATE_GRAY(element) ?
5945                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5946                     IS_EMC_GATE_GRAY(element) ?
5947                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5948                     element);
5949       DrawLevelField(x, y);
5950     }
5951     else if (IS_GATE_GRAY_ACTIVE(element) &&
5952              game.magnify_time_left == 0)
5953     {
5954       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5955                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5956                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5957                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5958                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5959                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5960                     element);
5961       DrawLevelField(x, y);
5962     }
5963   }
5964 }
5965
5966 static void ToggleLightSwitch(int x, int y)
5967 {
5968   int element = Feld[x][y];
5969
5970   game.light_time_left =
5971     (element == EL_LIGHT_SWITCH ?
5972      level.time_light * FRAMES_PER_SECOND : 0);
5973
5974   RedrawAllLightSwitchesAndInvisibleElements();
5975 }
5976
5977 static void ActivateTimegateSwitch(int x, int y)
5978 {
5979   int xx, yy;
5980
5981   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5982
5983   SCAN_PLAYFIELD(xx, yy)
5984   {
5985     int element = Feld[xx][yy];
5986
5987     if (element == EL_TIMEGATE_CLOSED ||
5988         element == EL_TIMEGATE_CLOSING)
5989     {
5990       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5991       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5992     }
5993
5994     /*
5995     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5996     {
5997       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5998       DrawLevelField(xx, yy);
5999     }
6000     */
6001
6002   }
6003
6004 #if 1
6005   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6006                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6007 #else
6008   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6009 #endif
6010 }
6011
6012 void Impact(int x, int y)
6013 {
6014   boolean last_line = (y == lev_fieldy - 1);
6015   boolean object_hit = FALSE;
6016   boolean impact = (last_line || object_hit);
6017   int element = Feld[x][y];
6018   int smashed = EL_STEELWALL;
6019
6020   if (!last_line)       /* check if element below was hit */
6021   {
6022     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6023       return;
6024
6025     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6026                                          MovDir[x][y + 1] != MV_DOWN ||
6027                                          MovPos[x][y + 1] <= TILEY / 2));
6028
6029     /* do not smash moving elements that left the smashed field in time */
6030     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6031         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6032       object_hit = FALSE;
6033
6034 #if USE_QUICKSAND_IMPACT_BUGFIX
6035     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6036     {
6037       RemoveMovingField(x, y + 1);
6038       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6039       Feld[x][y + 2] = EL_ROCK;
6040       DrawLevelField(x, y + 2);
6041
6042       object_hit = TRUE;
6043     }
6044
6045     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6046     {
6047       RemoveMovingField(x, y + 1);
6048       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6049       Feld[x][y + 2] = EL_ROCK;
6050       DrawLevelField(x, y + 2);
6051
6052       object_hit = TRUE;
6053     }
6054 #endif
6055
6056     if (object_hit)
6057       smashed = MovingOrBlocked2Element(x, y + 1);
6058
6059     impact = (last_line || object_hit);
6060   }
6061
6062   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6063   {
6064     SplashAcid(x, y + 1);
6065     return;
6066   }
6067
6068   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6069   /* only reset graphic animation if graphic really changes after impact */
6070   if (impact &&
6071       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6072   {
6073     ResetGfxAnimation(x, y);
6074     DrawLevelField(x, y);
6075   }
6076
6077   if (impact && CAN_EXPLODE_IMPACT(element))
6078   {
6079     Bang(x, y);
6080     return;
6081   }
6082   else if (impact && element == EL_PEARL &&
6083            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6084   {
6085     ResetGfxAnimation(x, y);
6086
6087     Feld[x][y] = EL_PEARL_BREAKING;
6088     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6089     return;
6090   }
6091   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6092   {
6093     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6094
6095     return;
6096   }
6097
6098   if (impact && element == EL_AMOEBA_DROP)
6099   {
6100     if (object_hit && IS_PLAYER(x, y + 1))
6101       KillPlayerUnlessEnemyProtected(x, y + 1);
6102     else if (object_hit && smashed == EL_PENGUIN)
6103       Bang(x, y + 1);
6104     else
6105     {
6106       Feld[x][y] = EL_AMOEBA_GROWING;
6107       Store[x][y] = EL_AMOEBA_WET;
6108
6109       ResetRandomAnimationValue(x, y);
6110     }
6111     return;
6112   }
6113
6114   if (object_hit)               /* check which object was hit */
6115   {
6116     if ((CAN_PASS_MAGIC_WALL(element) && 
6117          (smashed == EL_MAGIC_WALL ||
6118           smashed == EL_BD_MAGIC_WALL)) ||
6119         (CAN_PASS_DC_MAGIC_WALL(element) &&
6120          smashed == EL_DC_MAGIC_WALL))
6121     {
6122       int xx, yy;
6123       int activated_magic_wall =
6124         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6125          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6126          EL_DC_MAGIC_WALL_ACTIVE);
6127
6128       /* activate magic wall / mill */
6129       SCAN_PLAYFIELD(xx, yy)
6130       {
6131         if (Feld[xx][yy] == smashed)
6132           Feld[xx][yy] = activated_magic_wall;
6133       }
6134
6135       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6136       game.magic_wall_active = TRUE;
6137
6138       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6139                             SND_MAGIC_WALL_ACTIVATING :
6140                             smashed == EL_BD_MAGIC_WALL ?
6141                             SND_BD_MAGIC_WALL_ACTIVATING :
6142                             SND_DC_MAGIC_WALL_ACTIVATING));
6143     }
6144
6145     if (IS_PLAYER(x, y + 1))
6146     {
6147       if (CAN_SMASH_PLAYER(element))
6148       {
6149         KillPlayerUnlessEnemyProtected(x, y + 1);
6150         return;
6151       }
6152     }
6153     else if (smashed == EL_PENGUIN)
6154     {
6155       if (CAN_SMASH_PLAYER(element))
6156       {
6157         Bang(x, y + 1);
6158         return;
6159       }
6160     }
6161     else if (element == EL_BD_DIAMOND)
6162     {
6163       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6164       {
6165         Bang(x, y + 1);
6166         return;
6167       }
6168     }
6169     else if (((element == EL_SP_INFOTRON ||
6170                element == EL_SP_ZONK) &&
6171               (smashed == EL_SP_SNIKSNAK ||
6172                smashed == EL_SP_ELECTRON ||
6173                smashed == EL_SP_DISK_ORANGE)) ||
6174              (element == EL_SP_INFOTRON &&
6175               smashed == EL_SP_DISK_YELLOW))
6176     {
6177       Bang(x, y + 1);
6178       return;
6179     }
6180     else if (CAN_SMASH_EVERYTHING(element))
6181     {
6182       if (IS_CLASSIC_ENEMY(smashed) ||
6183           CAN_EXPLODE_SMASHED(smashed))
6184       {
6185         Bang(x, y + 1);
6186         return;
6187       }
6188       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6189       {
6190         if (smashed == EL_LAMP ||
6191             smashed == EL_LAMP_ACTIVE)
6192         {
6193           Bang(x, y + 1);
6194           return;
6195         }
6196         else if (smashed == EL_NUT)
6197         {
6198           Feld[x][y + 1] = EL_NUT_BREAKING;
6199           PlayLevelSound(x, y, SND_NUT_BREAKING);
6200           RaiseScoreElement(EL_NUT);
6201           return;
6202         }
6203         else if (smashed == EL_PEARL)
6204         {
6205           ResetGfxAnimation(x, y);
6206
6207           Feld[x][y + 1] = EL_PEARL_BREAKING;
6208           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6209           return;
6210         }
6211         else if (smashed == EL_DIAMOND)
6212         {
6213           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6214           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6215           return;
6216         }
6217         else if (IS_BELT_SWITCH(smashed))
6218         {
6219           ToggleBeltSwitch(x, y + 1);
6220         }
6221         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6222                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6223                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6224                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6225         {
6226           ToggleSwitchgateSwitch(x, y + 1);
6227         }
6228         else if (smashed == EL_LIGHT_SWITCH ||
6229                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6230         {
6231           ToggleLightSwitch(x, y + 1);
6232         }
6233         else
6234         {
6235 #if 0
6236           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6237 #endif
6238
6239           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6240
6241           CheckElementChangeBySide(x, y + 1, smashed, element,
6242                                    CE_SWITCHED, CH_SIDE_TOP);
6243           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6244                                             CH_SIDE_TOP);
6245         }
6246       }
6247       else
6248       {
6249         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6250       }
6251     }
6252   }
6253
6254   /* play sound of magic wall / mill */
6255   if (!last_line &&
6256       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6257        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6258        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6259   {
6260     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6261       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6262     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6263       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6264     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6265       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6266
6267     return;
6268   }
6269
6270   /* play sound of object that hits the ground */
6271   if (last_line || object_hit)
6272     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6273 }
6274
6275 inline static void TurnRoundExt(int x, int y)
6276 {
6277   static struct
6278   {
6279     int dx, dy;
6280   } move_xy[] =
6281   {
6282     {  0,  0 },
6283     { -1,  0 },
6284     { +1,  0 },
6285     {  0,  0 },
6286     {  0, -1 },
6287     {  0,  0 }, { 0, 0 }, { 0, 0 },
6288     {  0, +1 }
6289   };
6290   static struct
6291   {
6292     int left, right, back;
6293   } turn[] =
6294   {
6295     { 0,        0,              0        },
6296     { MV_DOWN,  MV_UP,          MV_RIGHT },
6297     { MV_UP,    MV_DOWN,        MV_LEFT  },
6298     { 0,        0,              0        },
6299     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6300     { 0,        0,              0        },
6301     { 0,        0,              0        },
6302     { 0,        0,              0        },
6303     { MV_RIGHT, MV_LEFT,        MV_UP    }
6304   };
6305
6306   int element = Feld[x][y];
6307   int move_pattern = element_info[element].move_pattern;
6308
6309   int old_move_dir = MovDir[x][y];
6310   int left_dir  = turn[old_move_dir].left;
6311   int right_dir = turn[old_move_dir].right;
6312   int back_dir  = turn[old_move_dir].back;
6313
6314   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6315   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6316   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6317   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6318
6319   int left_x  = x + left_dx,  left_y  = y + left_dy;
6320   int right_x = x + right_dx, right_y = y + right_dy;
6321   int move_x  = x + move_dx,  move_y  = y + move_dy;
6322
6323   int xx, yy;
6324
6325   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6326   {
6327     TestIfBadThingTouchesOtherBadThing(x, y);
6328
6329     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6330       MovDir[x][y] = right_dir;
6331     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6332       MovDir[x][y] = left_dir;
6333
6334     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6335       MovDelay[x][y] = 9;
6336     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6337       MovDelay[x][y] = 1;
6338   }
6339   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6340   {
6341     TestIfBadThingTouchesOtherBadThing(x, y);
6342
6343     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6344       MovDir[x][y] = left_dir;
6345     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6346       MovDir[x][y] = right_dir;
6347
6348     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6349       MovDelay[x][y] = 9;
6350     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6351       MovDelay[x][y] = 1;
6352   }
6353   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6354   {
6355     TestIfBadThingTouchesOtherBadThing(x, y);
6356
6357     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6358       MovDir[x][y] = left_dir;
6359     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6360       MovDir[x][y] = right_dir;
6361
6362     if (MovDir[x][y] != old_move_dir)
6363       MovDelay[x][y] = 9;
6364   }
6365   else if (element == EL_YAMYAM)
6366   {
6367     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6368     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6369
6370     if (can_turn_left && can_turn_right)
6371       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6372     else if (can_turn_left)
6373       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6374     else if (can_turn_right)
6375       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6376     else
6377       MovDir[x][y] = back_dir;
6378
6379     MovDelay[x][y] = 16 + 16 * RND(3);
6380   }
6381   else if (element == EL_DARK_YAMYAM)
6382   {
6383     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6384                                                          left_x, left_y);
6385     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6386                                                          right_x, right_y);
6387
6388     if (can_turn_left && can_turn_right)
6389       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6390     else if (can_turn_left)
6391       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6392     else if (can_turn_right)
6393       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6394     else
6395       MovDir[x][y] = back_dir;
6396
6397     MovDelay[x][y] = 16 + 16 * RND(3);
6398   }
6399   else if (element == EL_PACMAN)
6400   {
6401     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6402     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6403
6404     if (can_turn_left && can_turn_right)
6405       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6406     else if (can_turn_left)
6407       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6408     else if (can_turn_right)
6409       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6410     else
6411       MovDir[x][y] = back_dir;
6412
6413     MovDelay[x][y] = 6 + RND(40);
6414   }
6415   else if (element == EL_PIG)
6416   {
6417     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6418     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6419     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6420     boolean should_turn_left, should_turn_right, should_move_on;
6421     int rnd_value = 24;
6422     int rnd = RND(rnd_value);
6423
6424     should_turn_left = (can_turn_left &&
6425                         (!can_move_on ||
6426                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6427                                                    y + back_dy + left_dy)));
6428     should_turn_right = (can_turn_right &&
6429                          (!can_move_on ||
6430                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6431                                                     y + back_dy + right_dy)));
6432     should_move_on = (can_move_on &&
6433                       (!can_turn_left ||
6434                        !can_turn_right ||
6435                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6436                                                  y + move_dy + left_dy) ||
6437                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6438                                                  y + move_dy + right_dy)));
6439
6440     if (should_turn_left || should_turn_right || should_move_on)
6441     {
6442       if (should_turn_left && should_turn_right && should_move_on)
6443         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6444                         rnd < 2 * rnd_value / 3 ? right_dir :
6445                         old_move_dir);
6446       else if (should_turn_left && should_turn_right)
6447         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6448       else if (should_turn_left && should_move_on)
6449         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6450       else if (should_turn_right && should_move_on)
6451         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6452       else if (should_turn_left)
6453         MovDir[x][y] = left_dir;
6454       else if (should_turn_right)
6455         MovDir[x][y] = right_dir;
6456       else if (should_move_on)
6457         MovDir[x][y] = old_move_dir;
6458     }
6459     else if (can_move_on && rnd > rnd_value / 8)
6460       MovDir[x][y] = old_move_dir;
6461     else if (can_turn_left && can_turn_right)
6462       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6463     else if (can_turn_left && rnd > rnd_value / 8)
6464       MovDir[x][y] = left_dir;
6465     else if (can_turn_right && rnd > rnd_value/8)
6466       MovDir[x][y] = right_dir;
6467     else
6468       MovDir[x][y] = back_dir;
6469
6470     xx = x + move_xy[MovDir[x][y]].dx;
6471     yy = y + move_xy[MovDir[x][y]].dy;
6472
6473     if (!IN_LEV_FIELD(xx, yy) ||
6474         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6475       MovDir[x][y] = old_move_dir;
6476
6477     MovDelay[x][y] = 0;
6478   }
6479   else if (element == EL_DRAGON)
6480   {
6481     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6482     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6483     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6484     int rnd_value = 24;
6485     int rnd = RND(rnd_value);
6486
6487     if (can_move_on && rnd > rnd_value / 8)
6488       MovDir[x][y] = old_move_dir;
6489     else if (can_turn_left && can_turn_right)
6490       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6491     else if (can_turn_left && rnd > rnd_value / 8)
6492       MovDir[x][y] = left_dir;
6493     else if (can_turn_right && rnd > rnd_value / 8)
6494       MovDir[x][y] = right_dir;
6495     else
6496       MovDir[x][y] = back_dir;
6497
6498     xx = x + move_xy[MovDir[x][y]].dx;
6499     yy = y + move_xy[MovDir[x][y]].dy;
6500
6501     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6502       MovDir[x][y] = old_move_dir;
6503
6504     MovDelay[x][y] = 0;
6505   }
6506   else if (element == EL_MOLE)
6507   {
6508     boolean can_move_on =
6509       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6510                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6511                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6512     if (!can_move_on)
6513     {
6514       boolean can_turn_left =
6515         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6516                               IS_AMOEBOID(Feld[left_x][left_y])));
6517
6518       boolean can_turn_right =
6519         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6520                               IS_AMOEBOID(Feld[right_x][right_y])));
6521
6522       if (can_turn_left && can_turn_right)
6523         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6524       else if (can_turn_left)
6525         MovDir[x][y] = left_dir;
6526       else
6527         MovDir[x][y] = right_dir;
6528     }
6529
6530     if (MovDir[x][y] != old_move_dir)
6531       MovDelay[x][y] = 9;
6532   }
6533   else if (element == EL_BALLOON)
6534   {
6535     MovDir[x][y] = game.wind_direction;
6536     MovDelay[x][y] = 0;
6537   }
6538   else if (element == EL_SPRING)
6539   {
6540 #if USE_NEW_SPRING_BUMPER
6541     if (MovDir[x][y] & MV_HORIZONTAL)
6542     {
6543       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6544           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6545       {
6546         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6547         ResetGfxAnimation(move_x, move_y);
6548         DrawLevelField(move_x, move_y);
6549
6550         MovDir[x][y] = back_dir;
6551       }
6552       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6553                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6554         MovDir[x][y] = MV_NONE;
6555     }
6556 #else
6557     if (MovDir[x][y] & MV_HORIZONTAL &&
6558         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6559          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6560       MovDir[x][y] = MV_NONE;
6561 #endif
6562
6563     MovDelay[x][y] = 0;
6564   }
6565   else if (element == EL_ROBOT ||
6566            element == EL_SATELLITE ||
6567            element == EL_PENGUIN ||
6568            element == EL_EMC_ANDROID)
6569   {
6570     int attr_x = -1, attr_y = -1;
6571
6572     if (AllPlayersGone)
6573     {
6574       attr_x = ExitX;
6575       attr_y = ExitY;
6576     }
6577     else
6578     {
6579       int i;
6580
6581       for (i = 0; i < MAX_PLAYERS; i++)
6582       {
6583         struct PlayerInfo *player = &stored_player[i];
6584         int jx = player->jx, jy = player->jy;
6585
6586         if (!player->active)
6587           continue;
6588
6589         if (attr_x == -1 ||
6590             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6591         {
6592           attr_x = jx;
6593           attr_y = jy;
6594         }
6595       }
6596     }
6597
6598     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6599         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6600          game.engine_version < VERSION_IDENT(3,1,0,0)))
6601     {
6602       attr_x = ZX;
6603       attr_y = ZY;
6604     }
6605
6606     if (element == EL_PENGUIN)
6607     {
6608       int i;
6609       static int xy[4][2] =
6610       {
6611         { 0, -1 },
6612         { -1, 0 },
6613         { +1, 0 },
6614         { 0, +1 }
6615       };
6616
6617       for (i = 0; i < NUM_DIRECTIONS; i++)
6618       {
6619         int ex = x + xy[i][0];
6620         int ey = y + xy[i][1];
6621
6622         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6623                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6624                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6625                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6626         {
6627           attr_x = ex;
6628           attr_y = ey;
6629           break;
6630         }
6631       }
6632     }
6633
6634     MovDir[x][y] = MV_NONE;
6635     if (attr_x < x)
6636       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6637     else if (attr_x > x)
6638       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6639     if (attr_y < y)
6640       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6641     else if (attr_y > y)
6642       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6643
6644     if (element == EL_ROBOT)
6645     {
6646       int newx, newy;
6647
6648       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6649         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6650       Moving2Blocked(x, y, &newx, &newy);
6651
6652       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6653         MovDelay[x][y] = 8 + 8 * !RND(3);
6654       else
6655         MovDelay[x][y] = 16;
6656     }
6657     else if (element == EL_PENGUIN)
6658     {
6659       int newx, newy;
6660
6661       MovDelay[x][y] = 1;
6662
6663       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6664       {
6665         boolean first_horiz = RND(2);
6666         int new_move_dir = MovDir[x][y];
6667
6668         MovDir[x][y] =
6669           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6670         Moving2Blocked(x, y, &newx, &newy);
6671
6672         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6673           return;
6674
6675         MovDir[x][y] =
6676           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6677         Moving2Blocked(x, y, &newx, &newy);
6678
6679         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6680           return;
6681
6682         MovDir[x][y] = old_move_dir;
6683         return;
6684       }
6685     }
6686     else if (element == EL_SATELLITE)
6687     {
6688       int newx, newy;
6689
6690       MovDelay[x][y] = 1;
6691
6692       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6693       {
6694         boolean first_horiz = RND(2);
6695         int new_move_dir = MovDir[x][y];
6696
6697         MovDir[x][y] =
6698           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6699         Moving2Blocked(x, y, &newx, &newy);
6700
6701         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6702           return;
6703
6704         MovDir[x][y] =
6705           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6706         Moving2Blocked(x, y, &newx, &newy);
6707
6708         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6709           return;
6710
6711         MovDir[x][y] = old_move_dir;
6712         return;
6713       }
6714     }
6715     else if (element == EL_EMC_ANDROID)
6716     {
6717       static int check_pos[16] =
6718       {
6719         -1,             /*  0 => (invalid)          */
6720         7,              /*  1 => MV_LEFT            */
6721         3,              /*  2 => MV_RIGHT           */
6722         -1,             /*  3 => (invalid)          */
6723         1,              /*  4 =>            MV_UP   */
6724         0,              /*  5 => MV_LEFT  | MV_UP   */
6725         2,              /*  6 => MV_RIGHT | MV_UP   */
6726         -1,             /*  7 => (invalid)          */
6727         5,              /*  8 =>            MV_DOWN */
6728         6,              /*  9 => MV_LEFT  | MV_DOWN */
6729         4,              /* 10 => MV_RIGHT | MV_DOWN */
6730         -1,             /* 11 => (invalid)          */
6731         -1,             /* 12 => (invalid)          */
6732         -1,             /* 13 => (invalid)          */
6733         -1,             /* 14 => (invalid)          */
6734         -1,             /* 15 => (invalid)          */
6735       };
6736       static struct
6737       {
6738         int dx, dy;
6739         int dir;
6740       } check_xy[8] =
6741       {
6742         { -1, -1,       MV_LEFT  | MV_UP   },
6743         {  0, -1,                  MV_UP   },
6744         { +1, -1,       MV_RIGHT | MV_UP   },
6745         { +1,  0,       MV_RIGHT           },
6746         { +1, +1,       MV_RIGHT | MV_DOWN },
6747         {  0, +1,                  MV_DOWN },
6748         { -1, +1,       MV_LEFT  | MV_DOWN },
6749         { -1,  0,       MV_LEFT            },
6750       };
6751       int start_pos, check_order;
6752       boolean can_clone = FALSE;
6753       int i;
6754
6755       /* check if there is any free field around current position */
6756       for (i = 0; i < 8; i++)
6757       {
6758         int newx = x + check_xy[i].dx;
6759         int newy = y + check_xy[i].dy;
6760
6761         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6762         {
6763           can_clone = TRUE;
6764
6765           break;
6766         }
6767       }
6768
6769       if (can_clone)            /* randomly find an element to clone */
6770       {
6771         can_clone = FALSE;
6772
6773         start_pos = check_pos[RND(8)];
6774         check_order = (RND(2) ? -1 : +1);
6775
6776         for (i = 0; i < 8; i++)
6777         {
6778           int pos_raw = start_pos + i * check_order;
6779           int pos = (pos_raw + 8) % 8;
6780           int newx = x + check_xy[pos].dx;
6781           int newy = y + check_xy[pos].dy;
6782
6783           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6784           {
6785             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6786             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6787
6788             Store[x][y] = Feld[newx][newy];
6789
6790             can_clone = TRUE;
6791
6792             break;
6793           }
6794         }
6795       }
6796
6797       if (can_clone)            /* randomly find a direction to move */
6798       {
6799         can_clone = FALSE;
6800
6801         start_pos = check_pos[RND(8)];
6802         check_order = (RND(2) ? -1 : +1);
6803
6804         for (i = 0; i < 8; i++)
6805         {
6806           int pos_raw = start_pos + i * check_order;
6807           int pos = (pos_raw + 8) % 8;
6808           int newx = x + check_xy[pos].dx;
6809           int newy = y + check_xy[pos].dy;
6810           int new_move_dir = check_xy[pos].dir;
6811
6812           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6813           {
6814             MovDir[x][y] = new_move_dir;
6815             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6816
6817             can_clone = TRUE;
6818
6819             break;
6820           }
6821         }
6822       }
6823
6824       if (can_clone)            /* cloning and moving successful */
6825         return;
6826
6827       /* cannot clone -- try to move towards player */
6828
6829       start_pos = check_pos[MovDir[x][y] & 0x0f];
6830       check_order = (RND(2) ? -1 : +1);
6831
6832       for (i = 0; i < 3; i++)
6833       {
6834         /* first check start_pos, then previous/next or (next/previous) pos */
6835         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6836         int pos = (pos_raw + 8) % 8;
6837         int newx = x + check_xy[pos].dx;
6838         int newy = y + check_xy[pos].dy;
6839         int new_move_dir = check_xy[pos].dir;
6840
6841         if (IS_PLAYER(newx, newy))
6842           break;
6843
6844         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6845         {
6846           MovDir[x][y] = new_move_dir;
6847           MovDelay[x][y] = level.android_move_time * 8 + 1;
6848
6849           break;
6850         }
6851       }
6852     }
6853   }
6854   else if (move_pattern == MV_TURNING_LEFT ||
6855            move_pattern == MV_TURNING_RIGHT ||
6856            move_pattern == MV_TURNING_LEFT_RIGHT ||
6857            move_pattern == MV_TURNING_RIGHT_LEFT ||
6858            move_pattern == MV_TURNING_RANDOM ||
6859            move_pattern == MV_ALL_DIRECTIONS)
6860   {
6861     boolean can_turn_left =
6862       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6863     boolean can_turn_right =
6864       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6865
6866     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6867       return;
6868
6869     if (move_pattern == MV_TURNING_LEFT)
6870       MovDir[x][y] = left_dir;
6871     else if (move_pattern == MV_TURNING_RIGHT)
6872       MovDir[x][y] = right_dir;
6873     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6874       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6875     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6876       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6877     else if (move_pattern == MV_TURNING_RANDOM)
6878       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6879                       can_turn_right && !can_turn_left ? right_dir :
6880                       RND(2) ? left_dir : right_dir);
6881     else if (can_turn_left && can_turn_right)
6882       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6883     else if (can_turn_left)
6884       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6885     else if (can_turn_right)
6886       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6887     else
6888       MovDir[x][y] = back_dir;
6889
6890     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6891   }
6892   else if (move_pattern == MV_HORIZONTAL ||
6893            move_pattern == MV_VERTICAL)
6894   {
6895     if (move_pattern & old_move_dir)
6896       MovDir[x][y] = back_dir;
6897     else if (move_pattern == MV_HORIZONTAL)
6898       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6899     else if (move_pattern == MV_VERTICAL)
6900       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6901
6902     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6903   }
6904   else if (move_pattern & MV_ANY_DIRECTION)
6905   {
6906     MovDir[x][y] = move_pattern;
6907     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6908   }
6909   else if (move_pattern & MV_WIND_DIRECTION)
6910   {
6911     MovDir[x][y] = game.wind_direction;
6912     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6913   }
6914   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6915   {
6916     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6917       MovDir[x][y] = left_dir;
6918     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6919       MovDir[x][y] = right_dir;
6920
6921     if (MovDir[x][y] != old_move_dir)
6922       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6923   }
6924   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6925   {
6926     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6927       MovDir[x][y] = right_dir;
6928     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6929       MovDir[x][y] = left_dir;
6930
6931     if (MovDir[x][y] != old_move_dir)
6932       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6933   }
6934   else if (move_pattern == MV_TOWARDS_PLAYER ||
6935            move_pattern == MV_AWAY_FROM_PLAYER)
6936   {
6937     int attr_x = -1, attr_y = -1;
6938     int newx, newy;
6939     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6940
6941     if (AllPlayersGone)
6942     {
6943       attr_x = ExitX;
6944       attr_y = ExitY;
6945     }
6946     else
6947     {
6948       int i;
6949
6950       for (i = 0; i < MAX_PLAYERS; i++)
6951       {
6952         struct PlayerInfo *player = &stored_player[i];
6953         int jx = player->jx, jy = player->jy;
6954
6955         if (!player->active)
6956           continue;
6957
6958         if (attr_x == -1 ||
6959             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6960         {
6961           attr_x = jx;
6962           attr_y = jy;
6963         }
6964       }
6965     }
6966
6967     MovDir[x][y] = MV_NONE;
6968     if (attr_x < x)
6969       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6970     else if (attr_x > x)
6971       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6972     if (attr_y < y)
6973       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6974     else if (attr_y > y)
6975       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6976
6977     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6978
6979     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6980     {
6981       boolean first_horiz = RND(2);
6982       int new_move_dir = MovDir[x][y];
6983
6984       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6985       {
6986         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6987         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6988
6989         return;
6990       }
6991
6992       MovDir[x][y] =
6993         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6994       Moving2Blocked(x, y, &newx, &newy);
6995
6996       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6997         return;
6998
6999       MovDir[x][y] =
7000         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7001       Moving2Blocked(x, y, &newx, &newy);
7002
7003       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7004         return;
7005
7006       MovDir[x][y] = old_move_dir;
7007     }
7008   }
7009   else if (move_pattern == MV_WHEN_PUSHED ||
7010            move_pattern == MV_WHEN_DROPPED)
7011   {
7012     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7013       MovDir[x][y] = MV_NONE;
7014
7015     MovDelay[x][y] = 0;
7016   }
7017   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7018   {
7019     static int test_xy[7][2] =
7020     {
7021       { 0, -1 },
7022       { -1, 0 },
7023       { +1, 0 },
7024       { 0, +1 },
7025       { 0, -1 },
7026       { -1, 0 },
7027       { +1, 0 },
7028     };
7029     static int test_dir[7] =
7030     {
7031       MV_UP,
7032       MV_LEFT,
7033       MV_RIGHT,
7034       MV_DOWN,
7035       MV_UP,
7036       MV_LEFT,
7037       MV_RIGHT,
7038     };
7039     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7040     int move_preference = -1000000;     /* start with very low preference */
7041     int new_move_dir = MV_NONE;
7042     int start_test = RND(4);
7043     int i;
7044
7045     for (i = 0; i < NUM_DIRECTIONS; i++)
7046     {
7047       int move_dir = test_dir[start_test + i];
7048       int move_dir_preference;
7049
7050       xx = x + test_xy[start_test + i][0];
7051       yy = y + test_xy[start_test + i][1];
7052
7053       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7054           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7055       {
7056         new_move_dir = move_dir;
7057
7058         break;
7059       }
7060
7061       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7062         continue;
7063
7064       move_dir_preference = -1 * RunnerVisit[xx][yy];
7065       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7066         move_dir_preference = PlayerVisit[xx][yy];
7067
7068       if (move_dir_preference > move_preference)
7069       {
7070         /* prefer field that has not been visited for the longest time */
7071         move_preference = move_dir_preference;
7072         new_move_dir = move_dir;
7073       }
7074       else if (move_dir_preference == move_preference &&
7075                move_dir == old_move_dir)
7076       {
7077         /* prefer last direction when all directions are preferred equally */
7078         move_preference = move_dir_preference;
7079         new_move_dir = move_dir;
7080       }
7081     }
7082
7083     MovDir[x][y] = new_move_dir;
7084     if (old_move_dir != new_move_dir)
7085       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7086   }
7087 }
7088
7089 static void TurnRound(int x, int y)
7090 {
7091   int direction = MovDir[x][y];
7092
7093   TurnRoundExt(x, y);
7094
7095   GfxDir[x][y] = MovDir[x][y];
7096
7097   if (direction != MovDir[x][y])
7098     GfxFrame[x][y] = 0;
7099
7100   if (MovDelay[x][y])
7101     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7102
7103   ResetGfxFrame(x, y, FALSE);
7104 }
7105
7106 static boolean JustBeingPushed(int x, int y)
7107 {
7108   int i;
7109
7110   for (i = 0; i < MAX_PLAYERS; i++)
7111   {
7112     struct PlayerInfo *player = &stored_player[i];
7113
7114     if (player->active && player->is_pushing && player->MovPos)
7115     {
7116       int next_jx = player->jx + (player->jx - player->last_jx);
7117       int next_jy = player->jy + (player->jy - player->last_jy);
7118
7119       if (x == next_jx && y == next_jy)
7120         return TRUE;
7121     }
7122   }
7123
7124   return FALSE;
7125 }
7126
7127 void StartMoving(int x, int y)
7128 {
7129   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7130   int element = Feld[x][y];
7131
7132   if (Stop[x][y])
7133     return;
7134
7135   if (MovDelay[x][y] == 0)
7136     GfxAction[x][y] = ACTION_DEFAULT;
7137
7138   if (CAN_FALL(element) && y < lev_fieldy - 1)
7139   {
7140     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7141         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7142       if (JustBeingPushed(x, y))
7143         return;
7144
7145     if (element == EL_QUICKSAND_FULL)
7146     {
7147       if (IS_FREE(x, y + 1))
7148       {
7149         InitMovingField(x, y, MV_DOWN);
7150         started_moving = TRUE;
7151
7152         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7153 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7154         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7155           Store[x][y] = EL_ROCK;
7156 #else
7157         Store[x][y] = EL_ROCK;
7158 #endif
7159
7160         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7161       }
7162       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7163       {
7164         if (!MovDelay[x][y])
7165           MovDelay[x][y] = TILEY + 1;
7166
7167         if (MovDelay[x][y])
7168         {
7169           MovDelay[x][y]--;
7170           if (MovDelay[x][y])
7171             return;
7172         }
7173
7174         Feld[x][y] = EL_QUICKSAND_EMPTY;
7175         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7176         Store[x][y + 1] = Store[x][y];
7177         Store[x][y] = 0;
7178
7179         PlayLevelSoundAction(x, y, ACTION_FILLING);
7180       }
7181     }
7182     else if (element == EL_QUICKSAND_FAST_FULL)
7183     {
7184       if (IS_FREE(x, y + 1))
7185       {
7186         InitMovingField(x, y, MV_DOWN);
7187         started_moving = TRUE;
7188
7189         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7190 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7191         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7192           Store[x][y] = EL_ROCK;
7193 #else
7194         Store[x][y] = EL_ROCK;
7195 #endif
7196
7197         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7198       }
7199       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7200       {
7201         if (!MovDelay[x][y])
7202           MovDelay[x][y] = TILEY + 1;
7203
7204         if (MovDelay[x][y])
7205         {
7206           MovDelay[x][y]--;
7207           if (MovDelay[x][y])
7208             return;
7209         }
7210
7211         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7212         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7213         Store[x][y + 1] = Store[x][y];
7214         Store[x][y] = 0;
7215
7216         PlayLevelSoundAction(x, y, ACTION_FILLING);
7217       }
7218     }
7219     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7220              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7221     {
7222       InitMovingField(x, y, MV_DOWN);
7223       started_moving = TRUE;
7224
7225       Feld[x][y] = EL_QUICKSAND_FILLING;
7226       Store[x][y] = element;
7227
7228       PlayLevelSoundAction(x, y, ACTION_FILLING);
7229     }
7230     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7231              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7232     {
7233       InitMovingField(x, y, MV_DOWN);
7234       started_moving = TRUE;
7235
7236       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7237       Store[x][y] = element;
7238
7239       PlayLevelSoundAction(x, y, ACTION_FILLING);
7240     }
7241     else if (element == EL_MAGIC_WALL_FULL)
7242     {
7243       if (IS_FREE(x, y + 1))
7244       {
7245         InitMovingField(x, y, MV_DOWN);
7246         started_moving = TRUE;
7247
7248         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7249         Store[x][y] = EL_CHANGED(Store[x][y]);
7250       }
7251       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7252       {
7253         if (!MovDelay[x][y])
7254           MovDelay[x][y] = TILEY/4 + 1;
7255
7256         if (MovDelay[x][y])
7257         {
7258           MovDelay[x][y]--;
7259           if (MovDelay[x][y])
7260             return;
7261         }
7262
7263         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7264         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7265         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7266         Store[x][y] = 0;
7267       }
7268     }
7269     else if (element == EL_BD_MAGIC_WALL_FULL)
7270     {
7271       if (IS_FREE(x, y + 1))
7272       {
7273         InitMovingField(x, y, MV_DOWN);
7274         started_moving = TRUE;
7275
7276         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7277         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7278       }
7279       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7280       {
7281         if (!MovDelay[x][y])
7282           MovDelay[x][y] = TILEY/4 + 1;
7283
7284         if (MovDelay[x][y])
7285         {
7286           MovDelay[x][y]--;
7287           if (MovDelay[x][y])
7288             return;
7289         }
7290
7291         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7292         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7293         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7294         Store[x][y] = 0;
7295       }
7296     }
7297     else if (element == EL_DC_MAGIC_WALL_FULL)
7298     {
7299       if (IS_FREE(x, y + 1))
7300       {
7301         InitMovingField(x, y, MV_DOWN);
7302         started_moving = TRUE;
7303
7304         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7305         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7306       }
7307       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7308       {
7309         if (!MovDelay[x][y])
7310           MovDelay[x][y] = TILEY/4 + 1;
7311
7312         if (MovDelay[x][y])
7313         {
7314           MovDelay[x][y]--;
7315           if (MovDelay[x][y])
7316             return;
7317         }
7318
7319         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7320         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7321         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7322         Store[x][y] = 0;
7323       }
7324     }
7325     else if ((CAN_PASS_MAGIC_WALL(element) &&
7326               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7327                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7328              (CAN_PASS_DC_MAGIC_WALL(element) &&
7329               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7330
7331     {
7332       InitMovingField(x, y, MV_DOWN);
7333       started_moving = TRUE;
7334
7335       Feld[x][y] =
7336         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7337          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7338          EL_DC_MAGIC_WALL_FILLING);
7339       Store[x][y] = element;
7340     }
7341     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7342     {
7343       SplashAcid(x, y + 1);
7344
7345       InitMovingField(x, y, MV_DOWN);
7346       started_moving = TRUE;
7347
7348       Store[x][y] = EL_ACID;
7349     }
7350     else if (
7351 #if USE_FIX_IMPACT_COLLISION
7352              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7353               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7354 #else
7355              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7356               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7357 #endif
7358              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7359               CAN_FALL(element) && WasJustFalling[x][y] &&
7360               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7361
7362              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7363               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7364               (Feld[x][y + 1] == EL_BLOCKED)))
7365     {
7366       /* this is needed for a special case not covered by calling "Impact()"
7367          from "ContinueMoving()": if an element moves to a tile directly below
7368          another element which was just falling on that tile (which was empty
7369          in the previous frame), the falling element above would just stop
7370          instead of smashing the element below (in previous version, the above
7371          element was just checked for "moving" instead of "falling", resulting
7372          in incorrect smashes caused by horizontal movement of the above
7373          element; also, the case of the player being the element to smash was
7374          simply not covered here... :-/ ) */
7375
7376       CheckCollision[x][y] = 0;
7377       CheckImpact[x][y] = 0;
7378
7379       Impact(x, y);
7380     }
7381     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7382     {
7383       if (MovDir[x][y] == MV_NONE)
7384       {
7385         InitMovingField(x, y, MV_DOWN);
7386         started_moving = TRUE;
7387       }
7388     }
7389     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7390     {
7391       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7392         MovDir[x][y] = MV_DOWN;
7393
7394       InitMovingField(x, y, MV_DOWN);
7395       started_moving = TRUE;
7396     }
7397     else if (element == EL_AMOEBA_DROP)
7398     {
7399       Feld[x][y] = EL_AMOEBA_GROWING;
7400       Store[x][y] = EL_AMOEBA_WET;
7401     }
7402     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7403               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7404              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7405              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7406     {
7407       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7408                                 (IS_FREE(x - 1, y + 1) ||
7409                                  Feld[x - 1][y + 1] == EL_ACID));
7410       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7411                                 (IS_FREE(x + 1, y + 1) ||
7412                                  Feld[x + 1][y + 1] == EL_ACID));
7413       boolean can_fall_any  = (can_fall_left || can_fall_right);
7414       boolean can_fall_both = (can_fall_left && can_fall_right);
7415       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7416
7417 #if USE_NEW_ALL_SLIPPERY
7418       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7419       {
7420         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7421           can_fall_right = FALSE;
7422         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7423           can_fall_left = FALSE;
7424         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7425           can_fall_right = FALSE;
7426         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7427           can_fall_left = FALSE;
7428
7429         can_fall_any  = (can_fall_left || can_fall_right);
7430         can_fall_both = FALSE;
7431       }
7432 #else
7433       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7434       {
7435         if (slippery_type == SLIPPERY_ONLY_LEFT)
7436           can_fall_right = FALSE;
7437         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7438           can_fall_left = FALSE;
7439         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7440           can_fall_right = FALSE;
7441         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7442           can_fall_left = FALSE;
7443
7444         can_fall_any  = (can_fall_left || can_fall_right);
7445         can_fall_both = (can_fall_left && can_fall_right);
7446       }
7447 #endif
7448
7449 #if USE_NEW_ALL_SLIPPERY
7450 #else
7451 #if USE_NEW_SP_SLIPPERY
7452       /* !!! better use the same properties as for custom elements here !!! */
7453       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7454                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7455       {
7456         can_fall_right = FALSE;         /* slip down on left side */
7457         can_fall_both = FALSE;
7458       }
7459 #endif
7460 #endif
7461
7462 #if USE_NEW_ALL_SLIPPERY
7463       if (can_fall_both)
7464       {
7465         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7466           can_fall_right = FALSE;       /* slip down on left side */
7467         else
7468           can_fall_left = !(can_fall_right = RND(2));
7469
7470         can_fall_both = FALSE;
7471       }
7472 #else
7473       if (can_fall_both)
7474       {
7475         if (game.emulation == EMU_BOULDERDASH ||
7476             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7477           can_fall_right = FALSE;       /* slip down on left side */
7478         else
7479           can_fall_left = !(can_fall_right = RND(2));
7480
7481         can_fall_both = FALSE;
7482       }
7483 #endif
7484
7485       if (can_fall_any)
7486       {
7487         /* if not determined otherwise, prefer left side for slipping down */
7488         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7489         started_moving = TRUE;
7490       }
7491     }
7492 #if 0
7493     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7494 #else
7495     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7496 #endif
7497     {
7498       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7499       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7500       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7501       int belt_dir = game.belt_dir[belt_nr];
7502
7503       if ((belt_dir == MV_LEFT  && left_is_free) ||
7504           (belt_dir == MV_RIGHT && right_is_free))
7505       {
7506         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7507
7508         InitMovingField(x, y, belt_dir);
7509         started_moving = TRUE;
7510
7511         Pushed[x][y] = TRUE;
7512         Pushed[nextx][y] = TRUE;
7513
7514         GfxAction[x][y] = ACTION_DEFAULT;
7515       }
7516       else
7517       {
7518         MovDir[x][y] = 0;       /* if element was moving, stop it */
7519       }
7520     }
7521   }
7522
7523   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7524 #if 0
7525   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7526 #else
7527   if (CAN_MOVE(element) && !started_moving)
7528 #endif
7529   {
7530     int move_pattern = element_info[element].move_pattern;
7531     int newx, newy;
7532
7533 #if 0
7534 #if DEBUG
7535     if (MovDir[x][y] == MV_NONE)
7536     {
7537       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7538              x, y, element, element_info[element].token_name);
7539       printf("StartMoving(): This should never happen!\n");
7540     }
7541 #endif
7542 #endif
7543
7544     Moving2Blocked(x, y, &newx, &newy);
7545
7546     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7547       return;
7548
7549     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7550         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7551     {
7552       WasJustMoving[x][y] = 0;
7553       CheckCollision[x][y] = 0;
7554
7555       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7556
7557       if (Feld[x][y] != element)        /* element has changed */
7558         return;
7559     }
7560
7561     if (!MovDelay[x][y])        /* start new movement phase */
7562     {
7563       /* all objects that can change their move direction after each step
7564          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7565
7566       if (element != EL_YAMYAM &&
7567           element != EL_DARK_YAMYAM &&
7568           element != EL_PACMAN &&
7569           !(move_pattern & MV_ANY_DIRECTION) &&
7570           move_pattern != MV_TURNING_LEFT &&
7571           move_pattern != MV_TURNING_RIGHT &&
7572           move_pattern != MV_TURNING_LEFT_RIGHT &&
7573           move_pattern != MV_TURNING_RIGHT_LEFT &&
7574           move_pattern != MV_TURNING_RANDOM)
7575       {
7576         TurnRound(x, y);
7577
7578         if (MovDelay[x][y] && (element == EL_BUG ||
7579                                element == EL_SPACESHIP ||
7580                                element == EL_SP_SNIKSNAK ||
7581                                element == EL_SP_ELECTRON ||
7582                                element == EL_MOLE))
7583           DrawLevelField(x, y);
7584       }
7585     }
7586
7587     if (MovDelay[x][y])         /* wait some time before next movement */
7588     {
7589       MovDelay[x][y]--;
7590
7591       if (element == EL_ROBOT ||
7592           element == EL_YAMYAM ||
7593           element == EL_DARK_YAMYAM)
7594       {
7595         DrawLevelElementAnimationIfNeeded(x, y, element);
7596         PlayLevelSoundAction(x, y, ACTION_WAITING);
7597       }
7598       else if (element == EL_SP_ELECTRON)
7599         DrawLevelElementAnimationIfNeeded(x, y, element);
7600       else if (element == EL_DRAGON)
7601       {
7602         int i;
7603         int dir = MovDir[x][y];
7604         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7605         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7606         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7607                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7608                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7609                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7610         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7611
7612         GfxAction[x][y] = ACTION_ATTACKING;
7613
7614         if (IS_PLAYER(x, y))
7615           DrawPlayerField(x, y);
7616         else
7617           DrawLevelField(x, y);
7618
7619         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7620
7621         for (i = 1; i <= 3; i++)
7622         {
7623           int xx = x + i * dx;
7624           int yy = y + i * dy;
7625           int sx = SCREENX(xx);
7626           int sy = SCREENY(yy);
7627           int flame_graphic = graphic + (i - 1);
7628
7629           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7630             break;
7631
7632           if (MovDelay[x][y])
7633           {
7634             int flamed = MovingOrBlocked2Element(xx, yy);
7635
7636             /* !!! */
7637 #if 0
7638             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7639               Bang(xx, yy);
7640             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7641               RemoveMovingField(xx, yy);
7642             else
7643               RemoveField(xx, yy);
7644 #else
7645             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7646               Bang(xx, yy);
7647             else
7648               RemoveMovingField(xx, yy);
7649 #endif
7650
7651             ChangeDelay[xx][yy] = 0;
7652
7653             Feld[xx][yy] = EL_FLAMES;
7654
7655             if (IN_SCR_FIELD(sx, sy))
7656             {
7657               DrawLevelFieldCrumbledSand(xx, yy);
7658               DrawGraphic(sx, sy, flame_graphic, frame);
7659             }
7660           }
7661           else
7662           {
7663             if (Feld[xx][yy] == EL_FLAMES)
7664               Feld[xx][yy] = EL_EMPTY;
7665             DrawLevelField(xx, yy);
7666           }
7667         }
7668       }
7669
7670       if (MovDelay[x][y])       /* element still has to wait some time */
7671       {
7672         PlayLevelSoundAction(x, y, ACTION_WAITING);
7673
7674         return;
7675       }
7676     }
7677
7678     /* now make next step */
7679
7680     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7681
7682     if (DONT_COLLIDE_WITH(element) &&
7683         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7684         !PLAYER_ENEMY_PROTECTED(newx, newy))
7685     {
7686       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7687
7688       return;
7689     }
7690
7691     else if (CAN_MOVE_INTO_ACID(element) &&
7692              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7693              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7694              (MovDir[x][y] == MV_DOWN ||
7695               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7696     {
7697       SplashAcid(newx, newy);
7698       Store[x][y] = EL_ACID;
7699     }
7700     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7701     {
7702       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7703           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7704           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7705           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7706       {
7707         RemoveField(x, y);
7708         DrawLevelField(x, y);
7709
7710         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7711         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7712           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7713
7714         local_player->friends_still_needed--;
7715         if (!local_player->friends_still_needed &&
7716             !local_player->GameOver && AllPlayersGone)
7717           PlayerWins(local_player);
7718
7719         return;
7720       }
7721       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7722       {
7723         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7724           DrawLevelField(newx, newy);
7725         else
7726           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7727       }
7728       else if (!IS_FREE(newx, newy))
7729       {
7730         GfxAction[x][y] = ACTION_WAITING;
7731
7732         if (IS_PLAYER(x, y))
7733           DrawPlayerField(x, y);
7734         else
7735           DrawLevelField(x, y);
7736
7737         return;
7738       }
7739     }
7740     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7741     {
7742       if (IS_FOOD_PIG(Feld[newx][newy]))
7743       {
7744         if (IS_MOVING(newx, newy))
7745           RemoveMovingField(newx, newy);
7746         else
7747         {
7748           Feld[newx][newy] = EL_EMPTY;
7749           DrawLevelField(newx, newy);
7750         }
7751
7752         PlayLevelSound(x, y, SND_PIG_DIGGING);
7753       }
7754       else if (!IS_FREE(newx, newy))
7755       {
7756         if (IS_PLAYER(x, y))
7757           DrawPlayerField(x, y);
7758         else
7759           DrawLevelField(x, y);
7760
7761         return;
7762       }
7763     }
7764     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7765     {
7766       if (Store[x][y] != EL_EMPTY)
7767       {
7768         boolean can_clone = FALSE;
7769         int xx, yy;
7770
7771         /* check if element to clone is still there */
7772         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7773         {
7774           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7775           {
7776             can_clone = TRUE;
7777
7778             break;
7779           }
7780         }
7781
7782         /* cannot clone or target field not free anymore -- do not clone */
7783         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7784           Store[x][y] = EL_EMPTY;
7785       }
7786
7787       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7788       {
7789         if (IS_MV_DIAGONAL(MovDir[x][y]))
7790         {
7791           int diagonal_move_dir = MovDir[x][y];
7792           int stored = Store[x][y];
7793           int change_delay = 8;
7794           int graphic;
7795
7796           /* android is moving diagonally */
7797
7798           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7799
7800           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7801           GfxElement[x][y] = EL_EMC_ANDROID;
7802           GfxAction[x][y] = ACTION_SHRINKING;
7803           GfxDir[x][y] = diagonal_move_dir;
7804           ChangeDelay[x][y] = change_delay;
7805
7806           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7807                                    GfxDir[x][y]);
7808
7809           DrawLevelGraphicAnimation(x, y, graphic);
7810           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7811
7812           if (Feld[newx][newy] == EL_ACID)
7813           {
7814             SplashAcid(newx, newy);
7815
7816             return;
7817           }
7818
7819           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7820
7821           Store[newx][newy] = EL_EMC_ANDROID;
7822           GfxElement[newx][newy] = EL_EMC_ANDROID;
7823           GfxAction[newx][newy] = ACTION_GROWING;
7824           GfxDir[newx][newy] = diagonal_move_dir;
7825           ChangeDelay[newx][newy] = change_delay;
7826
7827           graphic = el_act_dir2img(GfxElement[newx][newy],
7828                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7829
7830           DrawLevelGraphicAnimation(newx, newy, graphic);
7831           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7832
7833           return;
7834         }
7835         else
7836         {
7837           Feld[newx][newy] = EL_EMPTY;
7838           DrawLevelField(newx, newy);
7839
7840           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7841         }
7842       }
7843       else if (!IS_FREE(newx, newy))
7844       {
7845 #if 0
7846         if (IS_PLAYER(x, y))
7847           DrawPlayerField(x, y);
7848         else
7849           DrawLevelField(x, y);
7850 #endif
7851
7852         return;
7853       }
7854     }
7855     else if (IS_CUSTOM_ELEMENT(element) &&
7856              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7857     {
7858       int new_element = Feld[newx][newy];
7859
7860       if (!IS_FREE(newx, newy))
7861       {
7862         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
7863                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
7864                       ACTION_BREAKING);
7865
7866         /* no element can dig solid indestructible elements */
7867         if (IS_INDESTRUCTIBLE(new_element) &&
7868             !IS_DIGGABLE(new_element) &&
7869             !IS_COLLECTIBLE(new_element))
7870           return;
7871
7872         if (AmoebaNr[newx][newy] &&
7873             (new_element == EL_AMOEBA_FULL ||
7874              new_element == EL_BD_AMOEBA ||
7875              new_element == EL_AMOEBA_GROWING))
7876         {
7877           AmoebaCnt[AmoebaNr[newx][newy]]--;
7878           AmoebaCnt2[AmoebaNr[newx][newy]]--;
7879         }
7880
7881         if (IS_MOVING(newx, newy))
7882           RemoveMovingField(newx, newy);
7883         else
7884         {
7885           RemoveField(newx, newy);
7886           DrawLevelField(newx, newy);
7887         }
7888
7889         /* if digged element was about to explode, prevent the explosion */
7890         ExplodeField[newx][newy] = EX_TYPE_NONE;
7891
7892         PlayLevelSoundAction(x, y, action);
7893       }
7894
7895       Store[newx][newy] = EL_EMPTY;
7896 #if 1
7897       /* this makes it possible to leave the removed element again */
7898       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7899         Store[newx][newy] = new_element;
7900 #else
7901       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
7902       {
7903         int move_leave_element = element_info[element].move_leave_element;
7904
7905         /* this makes it possible to leave the removed element again */
7906         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
7907                              new_element : move_leave_element);
7908       }
7909 #endif
7910
7911       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7912       {
7913         RunnerVisit[x][y] = FrameCounter;
7914         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7915       }
7916     }
7917     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7918     {
7919       if (!IS_FREE(newx, newy))
7920       {
7921         if (IS_PLAYER(x, y))
7922           DrawPlayerField(x, y);
7923         else
7924           DrawLevelField(x, y);
7925
7926         return;
7927       }
7928       else
7929       {
7930         boolean wanna_flame = !RND(10);
7931         int dx = newx - x, dy = newy - y;
7932         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7933         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7934         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7935                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7936         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7937                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7938
7939         if ((wanna_flame ||
7940              IS_CLASSIC_ENEMY(element1) ||
7941              IS_CLASSIC_ENEMY(element2)) &&
7942             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7943             element1 != EL_FLAMES && element2 != EL_FLAMES)
7944         {
7945           ResetGfxAnimation(x, y);
7946           GfxAction[x][y] = ACTION_ATTACKING;
7947
7948           if (IS_PLAYER(x, y))
7949             DrawPlayerField(x, y);
7950           else
7951             DrawLevelField(x, y);
7952
7953           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7954
7955           MovDelay[x][y] = 50;
7956
7957           /* !!! */
7958 #if 0
7959           RemoveField(newx, newy);
7960 #endif
7961           Feld[newx][newy] = EL_FLAMES;
7962           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7963           {
7964 #if 0
7965             RemoveField(newx1, newy1);
7966 #endif
7967             Feld[newx1][newy1] = EL_FLAMES;
7968           }
7969           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7970           {
7971 #if 0
7972             RemoveField(newx2, newy2);
7973 #endif
7974             Feld[newx2][newy2] = EL_FLAMES;
7975           }
7976
7977           return;
7978         }
7979       }
7980     }
7981     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7982              Feld[newx][newy] == EL_DIAMOND)
7983     {
7984       if (IS_MOVING(newx, newy))
7985         RemoveMovingField(newx, newy);
7986       else
7987       {
7988         Feld[newx][newy] = EL_EMPTY;
7989         DrawLevelField(newx, newy);
7990       }
7991
7992       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7993     }
7994     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7995              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7996     {
7997       if (AmoebaNr[newx][newy])
7998       {
7999         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8000         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8001             Feld[newx][newy] == EL_BD_AMOEBA)
8002           AmoebaCnt[AmoebaNr[newx][newy]]--;
8003       }
8004
8005 #if 0
8006       /* !!! test !!! */
8007       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8008       {
8009         RemoveMovingField(newx, newy);
8010       }
8011 #else
8012       if (IS_MOVING(newx, newy))
8013       {
8014         RemoveMovingField(newx, newy);
8015       }
8016 #endif
8017       else
8018       {
8019         Feld[newx][newy] = EL_EMPTY;
8020         DrawLevelField(newx, newy);
8021       }
8022
8023       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8024     }
8025     else if ((element == EL_PACMAN || element == EL_MOLE)
8026              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8027     {
8028       if (AmoebaNr[newx][newy])
8029       {
8030         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8031         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8032             Feld[newx][newy] == EL_BD_AMOEBA)
8033           AmoebaCnt[AmoebaNr[newx][newy]]--;
8034       }
8035
8036       if (element == EL_MOLE)
8037       {
8038         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8039         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8040
8041         ResetGfxAnimation(x, y);
8042         GfxAction[x][y] = ACTION_DIGGING;
8043         DrawLevelField(x, y);
8044
8045         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8046
8047         return;                         /* wait for shrinking amoeba */
8048       }
8049       else      /* element == EL_PACMAN */
8050       {
8051         Feld[newx][newy] = EL_EMPTY;
8052         DrawLevelField(newx, newy);
8053         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8054       }
8055     }
8056     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8057              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8058               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8059     {
8060       /* wait for shrinking amoeba to completely disappear */
8061       return;
8062     }
8063     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8064     {
8065       /* object was running against a wall */
8066
8067       TurnRound(x, y);
8068
8069 #if 0
8070       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8071       if (move_pattern & MV_ANY_DIRECTION &&
8072           move_pattern == MovDir[x][y])
8073       {
8074         int blocking_element =
8075           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8076
8077         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8078                                  MovDir[x][y]);
8079
8080         element = Feld[x][y];   /* element might have changed */
8081       }
8082 #endif
8083
8084       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8085         DrawLevelElementAnimation(x, y, element);
8086
8087       if (DONT_TOUCH(element))
8088         TestIfBadThingTouchesPlayer(x, y);
8089
8090       return;
8091     }
8092
8093     InitMovingField(x, y, MovDir[x][y]);
8094
8095     PlayLevelSoundAction(x, y, ACTION_MOVING);
8096   }
8097
8098   if (MovDir[x][y])
8099     ContinueMoving(x, y);
8100 }
8101
8102 void ContinueMoving(int x, int y)
8103 {
8104   int element = Feld[x][y];
8105   struct ElementInfo *ei = &element_info[element];
8106   int direction = MovDir[x][y];
8107   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8108   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8109   int newx = x + dx, newy = y + dy;
8110   int stored = Store[x][y];
8111   int stored_new = Store[newx][newy];
8112   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8113   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8114   boolean last_line = (newy == lev_fieldy - 1);
8115
8116   MovPos[x][y] += getElementMoveStepsize(x, y);
8117
8118   if (pushed_by_player) /* special case: moving object pushed by player */
8119     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8120
8121   if (ABS(MovPos[x][y]) < TILEX)
8122   {
8123 #if 0
8124     int ee = Feld[x][y];
8125     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8126     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8127
8128     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8129            x, y, ABS(MovPos[x][y]),
8130            ee, gg, ff,
8131            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8132 #endif
8133
8134     DrawLevelField(x, y);
8135
8136     return;     /* element is still moving */
8137   }
8138
8139   /* element reached destination field */
8140
8141   Feld[x][y] = EL_EMPTY;
8142   Feld[newx][newy] = element;
8143   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8144
8145   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8146   {
8147     element = Feld[newx][newy] = EL_ACID;
8148   }
8149   else if (element == EL_MOLE)
8150   {
8151     Feld[x][y] = EL_SAND;
8152
8153     DrawLevelFieldCrumbledSandNeighbours(x, y);
8154   }
8155   else if (element == EL_QUICKSAND_FILLING)
8156   {
8157     element = Feld[newx][newy] = get_next_element(element);
8158     Store[newx][newy] = Store[x][y];
8159   }
8160   else if (element == EL_QUICKSAND_EMPTYING)
8161   {
8162     Feld[x][y] = get_next_element(element);
8163     element = Feld[newx][newy] = Store[x][y];
8164   }
8165   else if (element == EL_QUICKSAND_FAST_FILLING)
8166   {
8167     element = Feld[newx][newy] = get_next_element(element);
8168     Store[newx][newy] = Store[x][y];
8169   }
8170   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8171   {
8172     Feld[x][y] = get_next_element(element);
8173     element = Feld[newx][newy] = Store[x][y];
8174   }
8175   else if (element == EL_MAGIC_WALL_FILLING)
8176   {
8177     element = Feld[newx][newy] = get_next_element(element);
8178     if (!game.magic_wall_active)
8179       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8180     Store[newx][newy] = Store[x][y];
8181   }
8182   else if (element == EL_MAGIC_WALL_EMPTYING)
8183   {
8184     Feld[x][y] = get_next_element(element);
8185     if (!game.magic_wall_active)
8186       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8187     element = Feld[newx][newy] = Store[x][y];
8188
8189 #if USE_NEW_CUSTOM_VALUE
8190     InitField(newx, newy, FALSE);
8191 #endif
8192   }
8193   else if (element == EL_BD_MAGIC_WALL_FILLING)
8194   {
8195     element = Feld[newx][newy] = get_next_element(element);
8196     if (!game.magic_wall_active)
8197       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8198     Store[newx][newy] = Store[x][y];
8199   }
8200   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8201   {
8202     Feld[x][y] = get_next_element(element);
8203     if (!game.magic_wall_active)
8204       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8205     element = Feld[newx][newy] = Store[x][y];
8206
8207 #if USE_NEW_CUSTOM_VALUE
8208     InitField(newx, newy, FALSE);
8209 #endif
8210   }
8211   else if (element == EL_DC_MAGIC_WALL_FILLING)
8212   {
8213     element = Feld[newx][newy] = get_next_element(element);
8214     if (!game.magic_wall_active)
8215       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8216     Store[newx][newy] = Store[x][y];
8217   }
8218   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8219   {
8220     Feld[x][y] = get_next_element(element);
8221     if (!game.magic_wall_active)
8222       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8223     element = Feld[newx][newy] = Store[x][y];
8224
8225 #if USE_NEW_CUSTOM_VALUE
8226     InitField(newx, newy, FALSE);
8227 #endif
8228   }
8229   else if (element == EL_AMOEBA_DROPPING)
8230   {
8231     Feld[x][y] = get_next_element(element);
8232     element = Feld[newx][newy] = Store[x][y];
8233   }
8234   else if (element == EL_SOKOBAN_OBJECT)
8235   {
8236     if (Back[x][y])
8237       Feld[x][y] = Back[x][y];
8238
8239     if (Back[newx][newy])
8240       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8241
8242     Back[x][y] = Back[newx][newy] = 0;
8243   }
8244
8245   Store[x][y] = EL_EMPTY;
8246   MovPos[x][y] = 0;
8247   MovDir[x][y] = 0;
8248   MovDelay[x][y] = 0;
8249
8250   MovDelay[newx][newy] = 0;
8251
8252   if (CAN_CHANGE_OR_HAS_ACTION(element))
8253   {
8254     /* copy element change control values to new field */
8255     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8256     ChangePage[newx][newy]  = ChangePage[x][y];
8257     ChangeCount[newx][newy] = ChangeCount[x][y];
8258     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8259   }
8260
8261 #if USE_NEW_CUSTOM_VALUE
8262     CustomValue[newx][newy] = CustomValue[x][y];
8263 #endif
8264
8265   ChangeDelay[x][y] = 0;
8266   ChangePage[x][y] = -1;
8267   ChangeCount[x][y] = 0;
8268   ChangeEvent[x][y] = -1;
8269
8270 #if USE_NEW_CUSTOM_VALUE
8271   CustomValue[x][y] = 0;
8272 #endif
8273
8274   /* copy animation control values to new field */
8275   GfxFrame[newx][newy]  = GfxFrame[x][y];
8276   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8277   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8278   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8279
8280   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8281
8282   /* some elements can leave other elements behind after moving */
8283 #if 1
8284   if (ei->move_leave_element != EL_EMPTY &&
8285       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8286       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8287 #else
8288   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8289       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8290       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8291 #endif
8292   {
8293     int move_leave_element = ei->move_leave_element;
8294
8295 #if 1
8296 #if 1
8297     /* this makes it possible to leave the removed element again */
8298     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8299       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8300 #else
8301     /* this makes it possible to leave the removed element again */
8302     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8303       move_leave_element = stored;
8304 #endif
8305 #else
8306     /* this makes it possible to leave the removed element again */
8307     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8308         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8309       move_leave_element = stored;
8310 #endif
8311
8312     Feld[x][y] = move_leave_element;
8313
8314     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8315       MovDir[x][y] = direction;
8316
8317     InitField(x, y, FALSE);
8318
8319     if (GFX_CRUMBLED(Feld[x][y]))
8320       DrawLevelFieldCrumbledSandNeighbours(x, y);
8321
8322     if (ELEM_IS_PLAYER(move_leave_element))
8323       RelocatePlayer(x, y, move_leave_element);
8324   }
8325
8326   /* do this after checking for left-behind element */
8327   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8328
8329   if (!CAN_MOVE(element) ||
8330       (CAN_FALL(element) && direction == MV_DOWN &&
8331        (element == EL_SPRING ||
8332         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8333         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8334     GfxDir[x][y] = MovDir[newx][newy] = 0;
8335
8336   DrawLevelField(x, y);
8337   DrawLevelField(newx, newy);
8338
8339   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8340
8341   /* prevent pushed element from moving on in pushed direction */
8342   if (pushed_by_player && CAN_MOVE(element) &&
8343       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8344       !(element_info[element].move_pattern & direction))
8345     TurnRound(newx, newy);
8346
8347   /* prevent elements on conveyor belt from moving on in last direction */
8348   if (pushed_by_conveyor && CAN_FALL(element) &&
8349       direction & MV_HORIZONTAL)
8350     MovDir[newx][newy] = 0;
8351
8352   if (!pushed_by_player)
8353   {
8354     int nextx = newx + dx, nexty = newy + dy;
8355     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8356
8357     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8358
8359     if (CAN_FALL(element) && direction == MV_DOWN)
8360       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8361
8362     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8363       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8364
8365 #if USE_FIX_IMPACT_COLLISION
8366     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8367       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8368 #endif
8369   }
8370
8371   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8372   {
8373     TestIfBadThingTouchesPlayer(newx, newy);
8374     TestIfBadThingTouchesFriend(newx, newy);
8375
8376     if (!IS_CUSTOM_ELEMENT(element))
8377       TestIfBadThingTouchesOtherBadThing(newx, newy);
8378   }
8379   else if (element == EL_PENGUIN)
8380     TestIfFriendTouchesBadThing(newx, newy);
8381
8382   /* give the player one last chance (one more frame) to move away */
8383   if (CAN_FALL(element) && direction == MV_DOWN &&
8384       (last_line || (!IS_FREE(x, newy + 1) &&
8385                      (!IS_PLAYER(x, newy + 1) ||
8386                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8387     Impact(x, newy);
8388
8389   if (pushed_by_player && !game.use_change_when_pushing_bug)
8390   {
8391     int push_side = MV_DIR_OPPOSITE(direction);
8392     struct PlayerInfo *player = PLAYERINFO(x, y);
8393
8394     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8395                                player->index_bit, push_side);
8396     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8397                                         player->index_bit, push_side);
8398   }
8399
8400   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8401     MovDelay[newx][newy] = 1;
8402
8403   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8404
8405   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8406
8407 #if 0
8408   if (ChangePage[newx][newy] != -1)             /* delayed change */
8409   {
8410     int page = ChangePage[newx][newy];
8411     struct ElementChangeInfo *change = &ei->change_page[page];
8412
8413     ChangePage[newx][newy] = -1;
8414
8415     if (change->can_change)
8416     {
8417       if (ChangeElement(newx, newy, element, page))
8418       {
8419         if (change->post_change_function)
8420           change->post_change_function(newx, newy);
8421       }
8422     }
8423
8424     if (change->has_action)
8425       ExecuteCustomElementAction(newx, newy, element, page);
8426   }
8427 #endif
8428
8429   TestIfElementHitsCustomElement(newx, newy, direction);
8430   TestIfPlayerTouchesCustomElement(newx, newy);
8431   TestIfElementTouchesCustomElement(newx, newy);
8432
8433   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8434       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8435     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8436                              MV_DIR_OPPOSITE(direction));
8437 }
8438
8439 int AmoebeNachbarNr(int ax, int ay)
8440 {
8441   int i;
8442   int element = Feld[ax][ay];
8443   int group_nr = 0;
8444   static int xy[4][2] =
8445   {
8446     { 0, -1 },
8447     { -1, 0 },
8448     { +1, 0 },
8449     { 0, +1 }
8450   };
8451
8452   for (i = 0; i < NUM_DIRECTIONS; i++)
8453   {
8454     int x = ax + xy[i][0];
8455     int y = ay + xy[i][1];
8456
8457     if (!IN_LEV_FIELD(x, y))
8458       continue;
8459
8460     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8461       group_nr = AmoebaNr[x][y];
8462   }
8463
8464   return group_nr;
8465 }
8466
8467 void AmoebenVereinigen(int ax, int ay)
8468 {
8469   int i, x, y, xx, yy;
8470   int new_group_nr = AmoebaNr[ax][ay];
8471   static int xy[4][2] =
8472   {
8473     { 0, -1 },
8474     { -1, 0 },
8475     { +1, 0 },
8476     { 0, +1 }
8477   };
8478
8479   if (new_group_nr == 0)
8480     return;
8481
8482   for (i = 0; i < NUM_DIRECTIONS; i++)
8483   {
8484     x = ax + xy[i][0];
8485     y = ay + xy[i][1];
8486
8487     if (!IN_LEV_FIELD(x, y))
8488       continue;
8489
8490     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8491          Feld[x][y] == EL_BD_AMOEBA ||
8492          Feld[x][y] == EL_AMOEBA_DEAD) &&
8493         AmoebaNr[x][y] != new_group_nr)
8494     {
8495       int old_group_nr = AmoebaNr[x][y];
8496
8497       if (old_group_nr == 0)
8498         return;
8499
8500       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8501       AmoebaCnt[old_group_nr] = 0;
8502       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8503       AmoebaCnt2[old_group_nr] = 0;
8504
8505       SCAN_PLAYFIELD(xx, yy)
8506       {
8507         if (AmoebaNr[xx][yy] == old_group_nr)
8508           AmoebaNr[xx][yy] = new_group_nr;
8509       }
8510     }
8511   }
8512 }
8513
8514 void AmoebeUmwandeln(int ax, int ay)
8515 {
8516   int i, x, y;
8517
8518   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8519   {
8520     int group_nr = AmoebaNr[ax][ay];
8521
8522 #ifdef DEBUG
8523     if (group_nr == 0)
8524     {
8525       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8526       printf("AmoebeUmwandeln(): This should never happen!\n");
8527       return;
8528     }
8529 #endif
8530
8531     SCAN_PLAYFIELD(x, y)
8532     {
8533       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8534       {
8535         AmoebaNr[x][y] = 0;
8536         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8537       }
8538     }
8539
8540     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8541                             SND_AMOEBA_TURNING_TO_GEM :
8542                             SND_AMOEBA_TURNING_TO_ROCK));
8543     Bang(ax, ay);
8544   }
8545   else
8546   {
8547     static int xy[4][2] =
8548     {
8549       { 0, -1 },
8550       { -1, 0 },
8551       { +1, 0 },
8552       { 0, +1 }
8553     };
8554
8555     for (i = 0; i < NUM_DIRECTIONS; i++)
8556     {
8557       x = ax + xy[i][0];
8558       y = ay + xy[i][1];
8559
8560       if (!IN_LEV_FIELD(x, y))
8561         continue;
8562
8563       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8564       {
8565         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8566                               SND_AMOEBA_TURNING_TO_GEM :
8567                               SND_AMOEBA_TURNING_TO_ROCK));
8568         Bang(x, y);
8569       }
8570     }
8571   }
8572 }
8573
8574 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8575 {
8576   int x, y;
8577   int group_nr = AmoebaNr[ax][ay];
8578   boolean done = FALSE;
8579
8580 #ifdef DEBUG
8581   if (group_nr == 0)
8582   {
8583     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8584     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8585     return;
8586   }
8587 #endif
8588
8589   SCAN_PLAYFIELD(x, y)
8590   {
8591     if (AmoebaNr[x][y] == group_nr &&
8592         (Feld[x][y] == EL_AMOEBA_DEAD ||
8593          Feld[x][y] == EL_BD_AMOEBA ||
8594          Feld[x][y] == EL_AMOEBA_GROWING))
8595     {
8596       AmoebaNr[x][y] = 0;
8597       Feld[x][y] = new_element;
8598       InitField(x, y, FALSE);
8599       DrawLevelField(x, y);
8600       done = TRUE;
8601     }
8602   }
8603
8604   if (done)
8605     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8606                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8607                             SND_BD_AMOEBA_TURNING_TO_GEM));
8608 }
8609
8610 void AmoebeWaechst(int x, int y)
8611 {
8612   static unsigned long sound_delay = 0;
8613   static unsigned long sound_delay_value = 0;
8614
8615   if (!MovDelay[x][y])          /* start new growing cycle */
8616   {
8617     MovDelay[x][y] = 7;
8618
8619     if (DelayReached(&sound_delay, sound_delay_value))
8620     {
8621       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8622       sound_delay_value = 30;
8623     }
8624   }
8625
8626   if (MovDelay[x][y])           /* wait some time before growing bigger */
8627   {
8628     MovDelay[x][y]--;
8629     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8630     {
8631       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8632                                            6 - MovDelay[x][y]);
8633
8634       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8635     }
8636
8637     if (!MovDelay[x][y])
8638     {
8639       Feld[x][y] = Store[x][y];
8640       Store[x][y] = 0;
8641       DrawLevelField(x, y);
8642     }
8643   }
8644 }
8645
8646 void AmoebaDisappearing(int x, int y)
8647 {
8648   static unsigned long sound_delay = 0;
8649   static unsigned long sound_delay_value = 0;
8650
8651   if (!MovDelay[x][y])          /* start new shrinking cycle */
8652   {
8653     MovDelay[x][y] = 7;
8654
8655     if (DelayReached(&sound_delay, sound_delay_value))
8656       sound_delay_value = 30;
8657   }
8658
8659   if (MovDelay[x][y])           /* wait some time before shrinking */
8660   {
8661     MovDelay[x][y]--;
8662     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8663     {
8664       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8665                                            6 - MovDelay[x][y]);
8666
8667       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8668     }
8669
8670     if (!MovDelay[x][y])
8671     {
8672       Feld[x][y] = EL_EMPTY;
8673       DrawLevelField(x, y);
8674
8675       /* don't let mole enter this field in this cycle;
8676          (give priority to objects falling to this field from above) */
8677       Stop[x][y] = TRUE;
8678     }
8679   }
8680 }
8681
8682 void AmoebeAbleger(int ax, int ay)
8683 {
8684   int i;
8685   int element = Feld[ax][ay];
8686   int graphic = el2img(element);
8687   int newax = ax, neway = ay;
8688   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8689   static int xy[4][2] =
8690   {
8691     { 0, -1 },
8692     { -1, 0 },
8693     { +1, 0 },
8694     { 0, +1 }
8695   };
8696
8697   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8698   {
8699     Feld[ax][ay] = EL_AMOEBA_DEAD;
8700     DrawLevelField(ax, ay);
8701     return;
8702   }
8703
8704   if (IS_ANIMATED(graphic))
8705     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8706
8707   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8708     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8709
8710   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8711   {
8712     MovDelay[ax][ay]--;
8713     if (MovDelay[ax][ay])
8714       return;
8715   }
8716
8717   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8718   {
8719     int start = RND(4);
8720     int x = ax + xy[start][0];
8721     int y = ay + xy[start][1];
8722
8723     if (!IN_LEV_FIELD(x, y))
8724       return;
8725
8726     if (IS_FREE(x, y) ||
8727         CAN_GROW_INTO(Feld[x][y]) ||
8728         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8729         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8730     {
8731       newax = x;
8732       neway = y;
8733     }
8734
8735     if (newax == ax && neway == ay)
8736       return;
8737   }
8738   else                          /* normal or "filled" (BD style) amoeba */
8739   {
8740     int start = RND(4);
8741     boolean waiting_for_player = FALSE;
8742
8743     for (i = 0; i < NUM_DIRECTIONS; i++)
8744     {
8745       int j = (start + i) % 4;
8746       int x = ax + xy[j][0];
8747       int y = ay + xy[j][1];
8748
8749       if (!IN_LEV_FIELD(x, y))
8750         continue;
8751
8752       if (IS_FREE(x, y) ||
8753           CAN_GROW_INTO(Feld[x][y]) ||
8754           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8755           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8756       {
8757         newax = x;
8758         neway = y;
8759         break;
8760       }
8761       else if (IS_PLAYER(x, y))
8762         waiting_for_player = TRUE;
8763     }
8764
8765     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8766     {
8767       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8768       {
8769         Feld[ax][ay] = EL_AMOEBA_DEAD;
8770         DrawLevelField(ax, ay);
8771         AmoebaCnt[AmoebaNr[ax][ay]]--;
8772
8773         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8774         {
8775           if (element == EL_AMOEBA_FULL)
8776             AmoebeUmwandeln(ax, ay);
8777           else if (element == EL_BD_AMOEBA)
8778             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8779         }
8780       }
8781       return;
8782     }
8783     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8784     {
8785       /* amoeba gets larger by growing in some direction */
8786
8787       int new_group_nr = AmoebaNr[ax][ay];
8788
8789 #ifdef DEBUG
8790   if (new_group_nr == 0)
8791   {
8792     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8793     printf("AmoebeAbleger(): This should never happen!\n");
8794     return;
8795   }
8796 #endif
8797
8798       AmoebaNr[newax][neway] = new_group_nr;
8799       AmoebaCnt[new_group_nr]++;
8800       AmoebaCnt2[new_group_nr]++;
8801
8802       /* if amoeba touches other amoeba(s) after growing, unify them */
8803       AmoebenVereinigen(newax, neway);
8804
8805       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8806       {
8807         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8808         return;
8809       }
8810     }
8811   }
8812
8813   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8814       (neway == lev_fieldy - 1 && newax != ax))
8815   {
8816     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8817     Store[newax][neway] = element;
8818   }
8819   else if (neway == ay || element == EL_EMC_DRIPPER)
8820   {
8821     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8822
8823     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8824   }
8825   else
8826   {
8827     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8828     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8829     Store[ax][ay] = EL_AMOEBA_DROP;
8830     ContinueMoving(ax, ay);
8831     return;
8832   }
8833
8834   DrawLevelField(newax, neway);
8835 }
8836
8837 void Life(int ax, int ay)
8838 {
8839   int x1, y1, x2, y2;
8840   int life_time = 40;
8841   int element = Feld[ax][ay];
8842   int graphic = el2img(element);
8843   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8844                          level.biomaze);
8845   boolean changed = FALSE;
8846
8847   if (IS_ANIMATED(graphic))
8848     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8849
8850   if (Stop[ax][ay])
8851     return;
8852
8853   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8854     MovDelay[ax][ay] = life_time;
8855
8856   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8857   {
8858     MovDelay[ax][ay]--;
8859     if (MovDelay[ax][ay])
8860       return;
8861   }
8862
8863   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8864   {
8865     int xx = ax+x1, yy = ay+y1;
8866     int nachbarn = 0;
8867
8868     if (!IN_LEV_FIELD(xx, yy))
8869       continue;
8870
8871     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8872     {
8873       int x = xx+x2, y = yy+y2;
8874
8875       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8876         continue;
8877
8878       if (((Feld[x][y] == element ||
8879             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8880            !Stop[x][y]) ||
8881           (IS_FREE(x, y) && Stop[x][y]))
8882         nachbarn++;
8883     }
8884
8885     if (xx == ax && yy == ay)           /* field in the middle */
8886     {
8887       if (nachbarn < life_parameter[0] ||
8888           nachbarn > life_parameter[1])
8889       {
8890         Feld[xx][yy] = EL_EMPTY;
8891         if (!Stop[xx][yy])
8892           DrawLevelField(xx, yy);
8893         Stop[xx][yy] = TRUE;
8894         changed = TRUE;
8895       }
8896     }
8897     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8898     {                                   /* free border field */
8899       if (nachbarn >= life_parameter[2] &&
8900           nachbarn <= life_parameter[3])
8901       {
8902         Feld[xx][yy] = element;
8903         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8904         if (!Stop[xx][yy])
8905           DrawLevelField(xx, yy);
8906         Stop[xx][yy] = TRUE;
8907         changed = TRUE;
8908       }
8909     }
8910   }
8911
8912   if (changed)
8913     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8914                    SND_GAME_OF_LIFE_GROWING);
8915 }
8916
8917 static void InitRobotWheel(int x, int y)
8918 {
8919   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8920 }
8921
8922 static void RunRobotWheel(int x, int y)
8923 {
8924   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8925 }
8926
8927 static void StopRobotWheel(int x, int y)
8928 {
8929   if (ZX == x && ZY == y)
8930   {
8931     ZX = ZY = -1;
8932
8933     game.robot_wheel_active = FALSE;
8934   }
8935 }
8936
8937 static void InitTimegateWheel(int x, int y)
8938 {
8939   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8940 }
8941
8942 static void RunTimegateWheel(int x, int y)
8943 {
8944   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8945 }
8946
8947 static void InitMagicBallDelay(int x, int y)
8948 {
8949 #if 1
8950   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8951 #else
8952   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
8953 #endif
8954 }
8955
8956 static void ActivateMagicBall(int bx, int by)
8957 {
8958   int x, y;
8959
8960   if (level.ball_random)
8961   {
8962     int pos_border = RND(8);    /* select one of the eight border elements */
8963     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8964     int xx = pos_content % 3;
8965     int yy = pos_content / 3;
8966
8967     x = bx - 1 + xx;
8968     y = by - 1 + yy;
8969
8970     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8971       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8972   }
8973   else
8974   {
8975     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8976     {
8977       int xx = x - bx + 1;
8978       int yy = y - by + 1;
8979
8980       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8981         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8982     }
8983   }
8984
8985   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8986 }
8987
8988 void CheckExit(int x, int y)
8989 {
8990   if (local_player->gems_still_needed > 0 ||
8991       local_player->sokobanfields_still_needed > 0 ||
8992       local_player->lights_still_needed > 0)
8993   {
8994     int element = Feld[x][y];
8995     int graphic = el2img(element);
8996
8997     if (IS_ANIMATED(graphic))
8998       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8999
9000     return;
9001   }
9002
9003   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9004     return;
9005
9006   Feld[x][y] = EL_EXIT_OPENING;
9007
9008   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9009 }
9010
9011 void CheckExitEM(int x, int y)
9012 {
9013   if (local_player->gems_still_needed > 0 ||
9014       local_player->sokobanfields_still_needed > 0 ||
9015       local_player->lights_still_needed > 0)
9016   {
9017     int element = Feld[x][y];
9018     int graphic = el2img(element);
9019
9020     if (IS_ANIMATED(graphic))
9021       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9022
9023     return;
9024   }
9025
9026   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9027     return;
9028
9029   Feld[x][y] = EL_EM_EXIT_OPENING;
9030
9031   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9032 }
9033
9034 void CheckExitSteel(int x, int y)
9035 {
9036   if (local_player->gems_still_needed > 0 ||
9037       local_player->sokobanfields_still_needed > 0 ||
9038       local_player->lights_still_needed > 0)
9039   {
9040     int element = Feld[x][y];
9041     int graphic = el2img(element);
9042
9043     if (IS_ANIMATED(graphic))
9044       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9045
9046     return;
9047   }
9048
9049   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9050     return;
9051
9052   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9053
9054   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9055 }
9056
9057 void CheckExitSteelEM(int x, int y)
9058 {
9059   if (local_player->gems_still_needed > 0 ||
9060       local_player->sokobanfields_still_needed > 0 ||
9061       local_player->lights_still_needed > 0)
9062   {
9063     int element = Feld[x][y];
9064     int graphic = el2img(element);
9065
9066     if (IS_ANIMATED(graphic))
9067       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9068
9069     return;
9070   }
9071
9072   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9073     return;
9074
9075   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9076
9077   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9078 }
9079
9080 void CheckExitSP(int x, int y)
9081 {
9082   if (local_player->gems_still_needed > 0)
9083   {
9084     int element = Feld[x][y];
9085     int graphic = el2img(element);
9086
9087     if (IS_ANIMATED(graphic))
9088       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9089
9090     return;
9091   }
9092
9093   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9094     return;
9095
9096   Feld[x][y] = EL_SP_EXIT_OPENING;
9097
9098   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9099 }
9100
9101 static void CloseAllOpenTimegates()
9102 {
9103   int x, y;
9104
9105   SCAN_PLAYFIELD(x, y)
9106   {
9107     int element = Feld[x][y];
9108
9109     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9110     {
9111       Feld[x][y] = EL_TIMEGATE_CLOSING;
9112
9113       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9114     }
9115   }
9116 }
9117
9118 void DrawTwinkleOnField(int x, int y)
9119 {
9120   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9121     return;
9122
9123   if (Feld[x][y] == EL_BD_DIAMOND)
9124     return;
9125
9126   if (MovDelay[x][y] == 0)      /* next animation frame */
9127     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9128
9129   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9130   {
9131     MovDelay[x][y]--;
9132
9133     if (setup.direct_draw && MovDelay[x][y])
9134       SetDrawtoField(DRAW_BUFFERED);
9135
9136     DrawLevelElementAnimation(x, y, Feld[x][y]);
9137
9138     if (MovDelay[x][y] != 0)
9139     {
9140       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9141                                            10 - MovDelay[x][y]);
9142
9143       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9144
9145       if (setup.direct_draw)
9146       {
9147         int dest_x, dest_y;
9148
9149         dest_x = FX + SCREENX(x) * TILEX;
9150         dest_y = FY + SCREENY(y) * TILEY;
9151
9152         BlitBitmap(drawto_field, window,
9153                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9154         SetDrawtoField(DRAW_DIRECT);
9155       }
9156     }
9157   }
9158 }
9159
9160 void MauerWaechst(int x, int y)
9161 {
9162   int delay = 6;
9163
9164   if (!MovDelay[x][y])          /* next animation frame */
9165     MovDelay[x][y] = 3 * delay;
9166
9167   if (MovDelay[x][y])           /* wait some time before next frame */
9168   {
9169     MovDelay[x][y]--;
9170
9171     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9172     {
9173       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9174       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9175
9176       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9177     }
9178
9179     if (!MovDelay[x][y])
9180     {
9181       if (MovDir[x][y] == MV_LEFT)
9182       {
9183         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9184           DrawLevelField(x - 1, y);
9185       }
9186       else if (MovDir[x][y] == MV_RIGHT)
9187       {
9188         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9189           DrawLevelField(x + 1, y);
9190       }
9191       else if (MovDir[x][y] == MV_UP)
9192       {
9193         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9194           DrawLevelField(x, y - 1);
9195       }
9196       else
9197       {
9198         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9199           DrawLevelField(x, y + 1);
9200       }
9201
9202       Feld[x][y] = Store[x][y];
9203       Store[x][y] = 0;
9204       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9205       DrawLevelField(x, y);
9206     }
9207   }
9208 }
9209
9210 void MauerAbleger(int ax, int ay)
9211 {
9212   int element = Feld[ax][ay];
9213   int graphic = el2img(element);
9214   boolean oben_frei = FALSE, unten_frei = FALSE;
9215   boolean links_frei = FALSE, rechts_frei = FALSE;
9216   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9217   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9218   boolean new_wall = FALSE;
9219
9220   if (IS_ANIMATED(graphic))
9221     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9222
9223   if (!MovDelay[ax][ay])        /* start building new wall */
9224     MovDelay[ax][ay] = 6;
9225
9226   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9227   {
9228     MovDelay[ax][ay]--;
9229     if (MovDelay[ax][ay])
9230       return;
9231   }
9232
9233   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9234     oben_frei = TRUE;
9235   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9236     unten_frei = TRUE;
9237   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9238     links_frei = TRUE;
9239   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9240     rechts_frei = TRUE;
9241
9242   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9243       element == EL_EXPANDABLE_WALL_ANY)
9244   {
9245     if (oben_frei)
9246     {
9247       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9248       Store[ax][ay-1] = element;
9249       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9250       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9251         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9252                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9253       new_wall = TRUE;
9254     }
9255     if (unten_frei)
9256     {
9257       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9258       Store[ax][ay+1] = element;
9259       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9260       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9261         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9262                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9263       new_wall = TRUE;
9264     }
9265   }
9266
9267   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9268       element == EL_EXPANDABLE_WALL_ANY ||
9269       element == EL_EXPANDABLE_WALL ||
9270       element == EL_BD_EXPANDABLE_WALL)
9271   {
9272     if (links_frei)
9273     {
9274       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9275       Store[ax-1][ay] = element;
9276       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9277       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9278         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9279                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9280       new_wall = TRUE;
9281     }
9282
9283     if (rechts_frei)
9284     {
9285       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9286       Store[ax+1][ay] = element;
9287       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9288       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9289         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9290                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9291       new_wall = TRUE;
9292     }
9293   }
9294
9295   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9296     DrawLevelField(ax, ay);
9297
9298   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9299     oben_massiv = TRUE;
9300   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9301     unten_massiv = TRUE;
9302   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9303     links_massiv = TRUE;
9304   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9305     rechts_massiv = TRUE;
9306
9307   if (((oben_massiv && unten_massiv) ||
9308        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9309        element == EL_EXPANDABLE_WALL) &&
9310       ((links_massiv && rechts_massiv) ||
9311        element == EL_EXPANDABLE_WALL_VERTICAL))
9312     Feld[ax][ay] = EL_WALL;
9313
9314   if (new_wall)
9315     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9316 }
9317
9318 void MauerAblegerStahl(int ax, int ay)
9319 {
9320   int element = Feld[ax][ay];
9321   int graphic = el2img(element);
9322   boolean oben_frei = FALSE, unten_frei = FALSE;
9323   boolean links_frei = FALSE, rechts_frei = FALSE;
9324   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9325   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9326   boolean new_wall = FALSE;
9327
9328   if (IS_ANIMATED(graphic))
9329     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9330
9331   if (!MovDelay[ax][ay])        /* start building new wall */
9332     MovDelay[ax][ay] = 6;
9333
9334   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9335   {
9336     MovDelay[ax][ay]--;
9337     if (MovDelay[ax][ay])
9338       return;
9339   }
9340
9341   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9342     oben_frei = TRUE;
9343   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9344     unten_frei = TRUE;
9345   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9346     links_frei = TRUE;
9347   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9348     rechts_frei = TRUE;
9349
9350   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9351       element == EL_EXPANDABLE_STEELWALL_ANY)
9352   {
9353     if (oben_frei)
9354     {
9355       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9356       Store[ax][ay-1] = element;
9357       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9358       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9359         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9360                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9361       new_wall = TRUE;
9362     }
9363     if (unten_frei)
9364     {
9365       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9366       Store[ax][ay+1] = element;
9367       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9368       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9369         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9370                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9371       new_wall = TRUE;
9372     }
9373   }
9374
9375   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9376       element == EL_EXPANDABLE_STEELWALL_ANY)
9377   {
9378     if (links_frei)
9379     {
9380       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9381       Store[ax-1][ay] = element;
9382       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9383       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9384         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9385                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9386       new_wall = TRUE;
9387     }
9388
9389     if (rechts_frei)
9390     {
9391       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9392       Store[ax+1][ay] = element;
9393       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9394       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9395         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9396                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9397       new_wall = TRUE;
9398     }
9399   }
9400
9401   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9402     oben_massiv = TRUE;
9403   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9404     unten_massiv = TRUE;
9405   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9406     links_massiv = TRUE;
9407   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9408     rechts_massiv = TRUE;
9409
9410   if (((oben_massiv && unten_massiv) ||
9411        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9412       ((links_massiv && rechts_massiv) ||
9413        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9414     Feld[ax][ay] = EL_WALL;
9415
9416   if (new_wall)
9417     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9418 }
9419
9420 void CheckForDragon(int x, int y)
9421 {
9422   int i, j;
9423   boolean dragon_found = FALSE;
9424   static int xy[4][2] =
9425   {
9426     { 0, -1 },
9427     { -1, 0 },
9428     { +1, 0 },
9429     { 0, +1 }
9430   };
9431
9432   for (i = 0; i < NUM_DIRECTIONS; i++)
9433   {
9434     for (j = 0; j < 4; j++)
9435     {
9436       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9437
9438       if (IN_LEV_FIELD(xx, yy) &&
9439           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9440       {
9441         if (Feld[xx][yy] == EL_DRAGON)
9442           dragon_found = TRUE;
9443       }
9444       else
9445         break;
9446     }
9447   }
9448
9449   if (!dragon_found)
9450   {
9451     for (i = 0; i < NUM_DIRECTIONS; i++)
9452     {
9453       for (j = 0; j < 3; j++)
9454       {
9455         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9456   
9457         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9458         {
9459           Feld[xx][yy] = EL_EMPTY;
9460           DrawLevelField(xx, yy);
9461         }
9462         else
9463           break;
9464       }
9465     }
9466   }
9467 }
9468
9469 static void InitBuggyBase(int x, int y)
9470 {
9471   int element = Feld[x][y];
9472   int activating_delay = FRAMES_PER_SECOND / 4;
9473
9474   ChangeDelay[x][y] =
9475     (element == EL_SP_BUGGY_BASE ?
9476      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9477      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9478      activating_delay :
9479      element == EL_SP_BUGGY_BASE_ACTIVE ?
9480      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9481 }
9482
9483 static void WarnBuggyBase(int x, int y)
9484 {
9485   int i;
9486   static int xy[4][2] =
9487   {
9488     { 0, -1 },
9489     { -1, 0 },
9490     { +1, 0 },
9491     { 0, +1 }
9492   };
9493
9494   for (i = 0; i < NUM_DIRECTIONS; i++)
9495   {
9496     int xx = x + xy[i][0];
9497     int yy = y + xy[i][1];
9498
9499     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9500     {
9501       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9502
9503       break;
9504     }
9505   }
9506 }
9507
9508 static void InitTrap(int x, int y)
9509 {
9510   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9511 }
9512
9513 static void ActivateTrap(int x, int y)
9514 {
9515   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9516 }
9517
9518 static void ChangeActiveTrap(int x, int y)
9519 {
9520   int graphic = IMG_TRAP_ACTIVE;
9521
9522   /* if new animation frame was drawn, correct crumbled sand border */
9523   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9524     DrawLevelFieldCrumbledSand(x, y);
9525 }
9526
9527 static int getSpecialActionElement(int element, int number, int base_element)
9528 {
9529   return (element != EL_EMPTY ? element :
9530           number != -1 ? base_element + number - 1 :
9531           EL_EMPTY);
9532 }
9533
9534 static int getModifiedActionNumber(int value_old, int operator, int operand,
9535                                    int value_min, int value_max)
9536 {
9537   int value_new = (operator == CA_MODE_SET      ? operand :
9538                    operator == CA_MODE_ADD      ? value_old + operand :
9539                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9540                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9541                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9542                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9543                    value_old);
9544
9545   return (value_new < value_min ? value_min :
9546           value_new > value_max ? value_max :
9547           value_new);
9548 }
9549
9550 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9551 {
9552   struct ElementInfo *ei = &element_info[element];
9553   struct ElementChangeInfo *change = &ei->change_page[page];
9554   int target_element = change->target_element;
9555   int action_type = change->action_type;
9556   int action_mode = change->action_mode;
9557   int action_arg = change->action_arg;
9558   int i;
9559
9560   if (!change->has_action)
9561     return;
9562
9563   /* ---------- determine action paramater values -------------------------- */
9564
9565   int level_time_value =
9566     (level.time > 0 ? TimeLeft :
9567      TimePlayed);
9568
9569   int action_arg_element =
9570     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9571      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9572      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9573      EL_EMPTY);
9574
9575   int action_arg_direction =
9576     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9577      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9578      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9579      change->actual_trigger_side :
9580      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9581      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9582      MV_NONE);
9583
9584   int action_arg_number_min =
9585     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9586      CA_ARG_MIN);
9587
9588   int action_arg_number_max =
9589     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9590      action_type == CA_SET_LEVEL_GEMS ? 999 :
9591      action_type == CA_SET_LEVEL_TIME ? 9999 :
9592      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9593      action_type == CA_SET_CE_VALUE ? 9999 :
9594      action_type == CA_SET_CE_SCORE ? 9999 :
9595      CA_ARG_MAX);
9596
9597   int action_arg_number_reset =
9598     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9599      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9600      action_type == CA_SET_LEVEL_TIME ? level.time :
9601      action_type == CA_SET_LEVEL_SCORE ? 0 :
9602 #if USE_NEW_CUSTOM_VALUE
9603      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9604 #else
9605      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9606 #endif
9607      action_type == CA_SET_CE_SCORE ? 0 :
9608      0);
9609
9610   int action_arg_number =
9611     (action_arg <= CA_ARG_MAX ? action_arg :
9612      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9613      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9614      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9615      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9616      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9617      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9618 #if USE_NEW_CUSTOM_VALUE
9619      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9620 #else
9621      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9622 #endif
9623      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9624      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9625      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9626      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9627      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9628      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9629      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9630      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9631      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9632      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9633      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9634      -1);
9635
9636   int action_arg_number_old =
9637     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9638      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9639      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9640      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9641      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9642      0);
9643
9644   int action_arg_number_new =
9645     getModifiedActionNumber(action_arg_number_old,
9646                             action_mode, action_arg_number,
9647                             action_arg_number_min, action_arg_number_max);
9648
9649   int trigger_player_bits =
9650     (change->actual_trigger_player >= EL_PLAYER_1 &&
9651      change->actual_trigger_player <= EL_PLAYER_4 ?
9652      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9653      PLAYER_BITS_ANY);
9654
9655   int action_arg_player_bits =
9656     (action_arg >= CA_ARG_PLAYER_1 &&
9657      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9658      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9659      PLAYER_BITS_ANY);
9660
9661   /* ---------- execute action  -------------------------------------------- */
9662
9663   switch (action_type)
9664   {
9665     case CA_NO_ACTION:
9666     {
9667       return;
9668     }
9669
9670     /* ---------- level actions  ------------------------------------------- */
9671
9672     case CA_RESTART_LEVEL:
9673     {
9674       game.restart_level = TRUE;
9675
9676       break;
9677     }
9678
9679     case CA_SHOW_ENVELOPE:
9680     {
9681       int element = getSpecialActionElement(action_arg_element,
9682                                             action_arg_number, EL_ENVELOPE_1);
9683
9684       if (IS_ENVELOPE(element))
9685         local_player->show_envelope = element;
9686
9687       break;
9688     }
9689
9690     case CA_SET_LEVEL_TIME:
9691     {
9692       if (level.time > 0)       /* only modify limited time value */
9693       {
9694         TimeLeft = action_arg_number_new;
9695
9696 #if 1
9697         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9698
9699         DisplayGameControlValues();
9700 #else
9701         DrawGameValue_Time(TimeLeft);
9702 #endif
9703
9704         if (!TimeLeft && setup.time_limit)
9705           for (i = 0; i < MAX_PLAYERS; i++)
9706             KillPlayer(&stored_player[i]);
9707       }
9708
9709       break;
9710     }
9711
9712     case CA_SET_LEVEL_SCORE:
9713     {
9714       local_player->score = action_arg_number_new;
9715
9716 #if 1
9717       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9718
9719       DisplayGameControlValues();
9720 #else
9721       DrawGameValue_Score(local_player->score);
9722 #endif
9723
9724       break;
9725     }
9726
9727     case CA_SET_LEVEL_GEMS:
9728     {
9729       local_player->gems_still_needed = action_arg_number_new;
9730
9731 #if 1
9732       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
9733
9734       DisplayGameControlValues();
9735 #else
9736       DrawGameValue_Emeralds(local_player->gems_still_needed);
9737 #endif
9738
9739       break;
9740     }
9741
9742 #if !USE_PLAYER_GRAVITY
9743     case CA_SET_LEVEL_GRAVITY:
9744     {
9745       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
9746                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
9747                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
9748                       game.gravity);
9749       break;
9750     }
9751 #endif
9752
9753     case CA_SET_LEVEL_WIND:
9754     {
9755       game.wind_direction = action_arg_direction;
9756
9757       break;
9758     }
9759
9760     /* ---------- player actions  ------------------------------------------ */
9761
9762     case CA_MOVE_PLAYER:
9763     {
9764       /* automatically move to the next field in specified direction */
9765       for (i = 0; i < MAX_PLAYERS; i++)
9766         if (trigger_player_bits & (1 << i))
9767           stored_player[i].programmed_action = action_arg_direction;
9768
9769       break;
9770     }
9771
9772     case CA_EXIT_PLAYER:
9773     {
9774       for (i = 0; i < MAX_PLAYERS; i++)
9775         if (action_arg_player_bits & (1 << i))
9776           PlayerWins(&stored_player[i]);
9777
9778       break;
9779     }
9780
9781     case CA_KILL_PLAYER:
9782     {
9783       for (i = 0; i < MAX_PLAYERS; i++)
9784         if (action_arg_player_bits & (1 << i))
9785           KillPlayer(&stored_player[i]);
9786
9787       break;
9788     }
9789
9790     case CA_SET_PLAYER_KEYS:
9791     {
9792       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9793       int element = getSpecialActionElement(action_arg_element,
9794                                             action_arg_number, EL_KEY_1);
9795
9796       if (IS_KEY(element))
9797       {
9798         for (i = 0; i < MAX_PLAYERS; i++)
9799         {
9800           if (trigger_player_bits & (1 << i))
9801           {
9802             stored_player[i].key[KEY_NR(element)] = key_state;
9803
9804             DrawGameDoorValues();
9805           }
9806         }
9807       }
9808
9809       break;
9810     }
9811
9812     case CA_SET_PLAYER_SPEED:
9813     {
9814       for (i = 0; i < MAX_PLAYERS; i++)
9815       {
9816         if (trigger_player_bits & (1 << i))
9817         {
9818           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9819
9820           if (action_arg == CA_ARG_SPEED_FASTER &&
9821               stored_player[i].cannot_move)
9822           {
9823             action_arg_number = STEPSIZE_VERY_SLOW;
9824           }
9825           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9826                    action_arg == CA_ARG_SPEED_FASTER)
9827           {
9828             action_arg_number = 2;
9829             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9830                            CA_MODE_MULTIPLY);
9831           }
9832           else if (action_arg == CA_ARG_NUMBER_RESET)
9833           {
9834             action_arg_number = level.initial_player_stepsize[i];
9835           }
9836
9837           move_stepsize =
9838             getModifiedActionNumber(move_stepsize,
9839                                     action_mode,
9840                                     action_arg_number,
9841                                     action_arg_number_min,
9842                                     action_arg_number_max);
9843
9844           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9845         }
9846       }
9847
9848       break;
9849     }
9850
9851     case CA_SET_PLAYER_SHIELD:
9852     {
9853       for (i = 0; i < MAX_PLAYERS; i++)
9854       {
9855         if (trigger_player_bits & (1 << i))
9856         {
9857           if (action_arg == CA_ARG_SHIELD_OFF)
9858           {
9859             stored_player[i].shield_normal_time_left = 0;
9860             stored_player[i].shield_deadly_time_left = 0;
9861           }
9862           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9863           {
9864             stored_player[i].shield_normal_time_left = 999999;
9865           }
9866           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9867           {
9868             stored_player[i].shield_normal_time_left = 999999;
9869             stored_player[i].shield_deadly_time_left = 999999;
9870           }
9871         }
9872       }
9873
9874       break;
9875     }
9876
9877 #if USE_PLAYER_GRAVITY
9878     case CA_SET_PLAYER_GRAVITY:
9879     {
9880       for (i = 0; i < MAX_PLAYERS; i++)
9881       {
9882         if (trigger_player_bits & (1 << i))
9883         {
9884           stored_player[i].gravity =
9885             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9886              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9887              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9888              stored_player[i].gravity);
9889         }
9890       }
9891
9892       break;
9893     }
9894 #endif
9895
9896     case CA_SET_PLAYER_ARTWORK:
9897     {
9898       for (i = 0; i < MAX_PLAYERS; i++)
9899       {
9900         if (trigger_player_bits & (1 << i))
9901         {
9902           int artwork_element = action_arg_element;
9903
9904           if (action_arg == CA_ARG_ELEMENT_RESET)
9905             artwork_element =
9906               (level.use_artwork_element[i] ? level.artwork_element[i] :
9907                stored_player[i].element_nr);
9908
9909 #if USE_GFX_RESET_PLAYER_ARTWORK
9910           if (stored_player[i].artwork_element != artwork_element)
9911             stored_player[i].Frame = 0;
9912 #endif
9913
9914           stored_player[i].artwork_element = artwork_element;
9915
9916           SetPlayerWaiting(&stored_player[i], FALSE);
9917
9918           /* set number of special actions for bored and sleeping animation */
9919           stored_player[i].num_special_action_bored =
9920             get_num_special_action(artwork_element,
9921                                    ACTION_BORING_1, ACTION_BORING_LAST);
9922           stored_player[i].num_special_action_sleeping =
9923             get_num_special_action(artwork_element,
9924                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9925         }
9926       }
9927
9928       break;
9929     }
9930
9931     /* ---------- CE actions  ---------------------------------------------- */
9932
9933     case CA_SET_CE_VALUE:
9934     {
9935 #if USE_NEW_CUSTOM_VALUE
9936       int last_ce_value = CustomValue[x][y];
9937
9938       CustomValue[x][y] = action_arg_number_new;
9939
9940       if (CustomValue[x][y] != last_ce_value)
9941       {
9942         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9943         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9944
9945         if (CustomValue[x][y] == 0)
9946         {
9947           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9948           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9949         }
9950       }
9951 #endif
9952
9953       break;
9954     }
9955
9956     case CA_SET_CE_SCORE:
9957     {
9958 #if USE_NEW_CUSTOM_VALUE
9959       int last_ce_score = ei->collect_score;
9960
9961       ei->collect_score = action_arg_number_new;
9962
9963       if (ei->collect_score != last_ce_score)
9964       {
9965         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9966         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9967
9968         if (ei->collect_score == 0)
9969         {
9970           int xx, yy;
9971
9972           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9973           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9974
9975           /*
9976             This is a very special case that seems to be a mixture between
9977             CheckElementChange() and CheckTriggeredElementChange(): while
9978             the first one only affects single elements that are triggered
9979             directly, the second one affects multiple elements in the playfield
9980             that are triggered indirectly by another element. This is a third
9981             case: Changing the CE score always affects multiple identical CEs,
9982             so every affected CE must be checked, not only the single CE for
9983             which the CE score was changed in the first place (as every instance
9984             of that CE shares the same CE score, and therefore also can change)!
9985           */
9986           SCAN_PLAYFIELD(xx, yy)
9987           {
9988             if (Feld[xx][yy] == element)
9989               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9990                                  CE_SCORE_GETS_ZERO);
9991           }
9992         }
9993       }
9994 #endif
9995
9996       break;
9997     }
9998
9999     /* ---------- engine actions  ------------------------------------------ */
10000
10001     case CA_SET_ENGINE_SCAN_MODE:
10002     {
10003       InitPlayfieldScanMode(action_arg);
10004
10005       break;
10006     }
10007
10008     default:
10009       break;
10010   }
10011 }
10012
10013 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10014 {
10015   int old_element = Feld[x][y];
10016   int new_element = GetElementFromGroupElement(element);
10017   int previous_move_direction = MovDir[x][y];
10018 #if USE_NEW_CUSTOM_VALUE
10019   int last_ce_value = CustomValue[x][y];
10020 #endif
10021   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10022   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10023   boolean add_player_onto_element = (new_element_is_player &&
10024 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10025                                      /* this breaks SnakeBite when a snake is
10026                                         halfway through a door that closes */
10027                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10028                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10029 #endif
10030                                      IS_WALKABLE(old_element));
10031
10032 #if 0
10033   /* check if element under the player changes from accessible to unaccessible
10034      (needed for special case of dropping element which then changes) */
10035   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10036       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10037   {
10038     Bang(x, y);
10039
10040     return;
10041   }
10042 #endif
10043
10044   if (!add_player_onto_element)
10045   {
10046     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10047       RemoveMovingField(x, y);
10048     else
10049       RemoveField(x, y);
10050
10051     Feld[x][y] = new_element;
10052
10053 #if !USE_GFX_RESET_GFX_ANIMATION
10054     ResetGfxAnimation(x, y);
10055     ResetRandomAnimationValue(x, y);
10056 #endif
10057
10058     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10059       MovDir[x][y] = previous_move_direction;
10060
10061 #if USE_NEW_CUSTOM_VALUE
10062     if (element_info[new_element].use_last_ce_value)
10063       CustomValue[x][y] = last_ce_value;
10064 #endif
10065
10066     InitField_WithBug1(x, y, FALSE);
10067
10068     new_element = Feld[x][y];   /* element may have changed */
10069
10070 #if USE_GFX_RESET_GFX_ANIMATION
10071     ResetGfxAnimation(x, y);
10072     ResetRandomAnimationValue(x, y);
10073 #endif
10074
10075     DrawLevelField(x, y);
10076
10077     if (GFX_CRUMBLED(new_element))
10078       DrawLevelFieldCrumbledSandNeighbours(x, y);
10079   }
10080
10081 #if 1
10082   /* check if element under the player changes from accessible to unaccessible
10083      (needed for special case of dropping element which then changes) */
10084   /* (must be checked after creating new element for walkable group elements) */
10085 #if USE_FIX_KILLED_BY_NON_WALKABLE
10086   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10087       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10088   {
10089     Bang(x, y);
10090
10091     return;
10092   }
10093 #else
10094   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10095       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10096   {
10097     Bang(x, y);
10098
10099     return;
10100   }
10101 #endif
10102 #endif
10103
10104   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10105   if (new_element_is_player)
10106     RelocatePlayer(x, y, new_element);
10107
10108   if (is_change)
10109     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10110
10111   TestIfBadThingTouchesPlayer(x, y);
10112   TestIfPlayerTouchesCustomElement(x, y);
10113   TestIfElementTouchesCustomElement(x, y);
10114 }
10115
10116 static void CreateField(int x, int y, int element)
10117 {
10118   CreateFieldExt(x, y, element, FALSE);
10119 }
10120
10121 static void CreateElementFromChange(int x, int y, int element)
10122 {
10123   element = GET_VALID_RUNTIME_ELEMENT(element);
10124
10125 #if USE_STOP_CHANGED_ELEMENTS
10126   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10127   {
10128     int old_element = Feld[x][y];
10129
10130     /* prevent changed element from moving in same engine frame
10131        unless both old and new element can either fall or move */
10132     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10133         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10134       Stop[x][y] = TRUE;
10135   }
10136 #endif
10137
10138   CreateFieldExt(x, y, element, TRUE);
10139 }
10140
10141 static boolean ChangeElement(int x, int y, int element, int page)
10142 {
10143   struct ElementInfo *ei = &element_info[element];
10144   struct ElementChangeInfo *change = &ei->change_page[page];
10145   int ce_value = CustomValue[x][y];
10146   int ce_score = ei->collect_score;
10147   int target_element;
10148   int old_element = Feld[x][y];
10149
10150   /* always use default change event to prevent running into a loop */
10151   if (ChangeEvent[x][y] == -1)
10152     ChangeEvent[x][y] = CE_DELAY;
10153
10154   if (ChangeEvent[x][y] == CE_DELAY)
10155   {
10156     /* reset actual trigger element, trigger player and action element */
10157     change->actual_trigger_element = EL_EMPTY;
10158     change->actual_trigger_player = EL_PLAYER_1;
10159     change->actual_trigger_side = CH_SIDE_NONE;
10160     change->actual_trigger_ce_value = 0;
10161     change->actual_trigger_ce_score = 0;
10162   }
10163
10164   /* do not change elements more than a specified maximum number of changes */
10165   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10166     return FALSE;
10167
10168   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10169
10170   if (change->explode)
10171   {
10172     Bang(x, y);
10173
10174     return TRUE;
10175   }
10176
10177   if (change->use_target_content)
10178   {
10179     boolean complete_replace = TRUE;
10180     boolean can_replace[3][3];
10181     int xx, yy;
10182
10183     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10184     {
10185       boolean is_empty;
10186       boolean is_walkable;
10187       boolean is_diggable;
10188       boolean is_collectible;
10189       boolean is_removable;
10190       boolean is_destructible;
10191       int ex = x + xx - 1;
10192       int ey = y + yy - 1;
10193       int content_element = change->target_content.e[xx][yy];
10194       int e;
10195
10196       can_replace[xx][yy] = TRUE;
10197
10198       if (ex == x && ey == y)   /* do not check changing element itself */
10199         continue;
10200
10201       if (content_element == EL_EMPTY_SPACE)
10202       {
10203         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10204
10205         continue;
10206       }
10207
10208       if (!IN_LEV_FIELD(ex, ey))
10209       {
10210         can_replace[xx][yy] = FALSE;
10211         complete_replace = FALSE;
10212
10213         continue;
10214       }
10215
10216       e = Feld[ex][ey];
10217
10218       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10219         e = MovingOrBlocked2Element(ex, ey);
10220
10221       is_empty = (IS_FREE(ex, ey) ||
10222                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10223
10224       is_walkable     = (is_empty || IS_WALKABLE(e));
10225       is_diggable     = (is_empty || IS_DIGGABLE(e));
10226       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10227       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10228       is_removable    = (is_diggable || is_collectible);
10229
10230       can_replace[xx][yy] =
10231         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10232           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10233           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10234           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10235           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10236           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10237          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10238
10239       if (!can_replace[xx][yy])
10240         complete_replace = FALSE;
10241     }
10242
10243     if (!change->only_if_complete || complete_replace)
10244     {
10245       boolean something_has_changed = FALSE;
10246
10247       if (change->only_if_complete && change->use_random_replace &&
10248           RND(100) < change->random_percentage)
10249         return FALSE;
10250
10251       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10252       {
10253         int ex = x + xx - 1;
10254         int ey = y + yy - 1;
10255         int content_element;
10256
10257         if (can_replace[xx][yy] && (!change->use_random_replace ||
10258                                     RND(100) < change->random_percentage))
10259         {
10260           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10261             RemoveMovingField(ex, ey);
10262
10263           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10264
10265           content_element = change->target_content.e[xx][yy];
10266           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10267                                               ce_value, ce_score);
10268
10269           CreateElementFromChange(ex, ey, target_element);
10270
10271           something_has_changed = TRUE;
10272
10273           /* for symmetry reasons, freeze newly created border elements */
10274           if (ex != x || ey != y)
10275             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10276         }
10277       }
10278
10279       if (something_has_changed)
10280       {
10281         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10282         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10283       }
10284     }
10285   }
10286   else
10287   {
10288     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10289                                         ce_value, ce_score);
10290
10291     if (element == EL_DIAGONAL_GROWING ||
10292         element == EL_DIAGONAL_SHRINKING)
10293     {
10294       target_element = Store[x][y];
10295
10296       Store[x][y] = EL_EMPTY;
10297     }
10298
10299     CreateElementFromChange(x, y, target_element);
10300
10301     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10302     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10303   }
10304
10305   /* this uses direct change before indirect change */
10306   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10307
10308   return TRUE;
10309 }
10310
10311 #if USE_NEW_DELAYED_ACTION
10312
10313 static void HandleElementChange(int x, int y, int page)
10314 {
10315   int element = MovingOrBlocked2Element(x, y);
10316   struct ElementInfo *ei = &element_info[element];
10317   struct ElementChangeInfo *change = &ei->change_page[page];
10318
10319 #ifdef DEBUG
10320   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10321       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10322   {
10323     printf("\n\n");
10324     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10325            x, y, element, element_info[element].token_name);
10326     printf("HandleElementChange(): This should never happen!\n");
10327     printf("\n\n");
10328   }
10329 #endif
10330
10331   /* this can happen with classic bombs on walkable, changing elements */
10332   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10333   {
10334 #if 0
10335     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10336       ChangeDelay[x][y] = 0;
10337 #endif
10338
10339     return;
10340   }
10341
10342   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10343   {
10344     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10345
10346     if (change->can_change)
10347     {
10348 #if 1
10349       /* !!! not clear why graphic animation should be reset at all here !!! */
10350       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10351 #if USE_GFX_RESET_WHEN_NOT_MOVING
10352       /* when a custom element is about to change (for example by change delay),
10353          do not reset graphic animation when the custom element is moving */
10354       if (!IS_MOVING(x, y))
10355 #endif
10356       {
10357         ResetGfxAnimation(x, y);
10358         ResetRandomAnimationValue(x, y);
10359       }
10360 #endif
10361
10362       if (change->pre_change_function)
10363         change->pre_change_function(x, y);
10364     }
10365   }
10366
10367   ChangeDelay[x][y]--;
10368
10369   if (ChangeDelay[x][y] != 0)           /* continue element change */
10370   {
10371     if (change->can_change)
10372     {
10373       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10374
10375       if (IS_ANIMATED(graphic))
10376         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10377
10378       if (change->change_function)
10379         change->change_function(x, y);
10380     }
10381   }
10382   else                                  /* finish element change */
10383   {
10384     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10385     {
10386       page = ChangePage[x][y];
10387       ChangePage[x][y] = -1;
10388
10389       change = &ei->change_page[page];
10390     }
10391
10392     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10393     {
10394       ChangeDelay[x][y] = 1;            /* try change after next move step */
10395       ChangePage[x][y] = page;          /* remember page to use for change */
10396
10397       return;
10398     }
10399
10400     if (change->can_change)
10401     {
10402       if (ChangeElement(x, y, element, page))
10403       {
10404         if (change->post_change_function)
10405           change->post_change_function(x, y);
10406       }
10407     }
10408
10409     if (change->has_action)
10410       ExecuteCustomElementAction(x, y, element, page);
10411   }
10412 }
10413
10414 #else
10415
10416 static void HandleElementChange(int x, int y, int page)
10417 {
10418   int element = MovingOrBlocked2Element(x, y);
10419   struct ElementInfo *ei = &element_info[element];
10420   struct ElementChangeInfo *change = &ei->change_page[page];
10421
10422 #ifdef DEBUG
10423   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10424   {
10425     printf("\n\n");
10426     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10427            x, y, element, element_info[element].token_name);
10428     printf("HandleElementChange(): This should never happen!\n");
10429     printf("\n\n");
10430   }
10431 #endif
10432
10433   /* this can happen with classic bombs on walkable, changing elements */
10434   if (!CAN_CHANGE(element))
10435   {
10436 #if 0
10437     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10438       ChangeDelay[x][y] = 0;
10439 #endif
10440
10441     return;
10442   }
10443
10444   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10445   {
10446     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10447
10448     ResetGfxAnimation(x, y);
10449     ResetRandomAnimationValue(x, y);
10450
10451     if (change->pre_change_function)
10452       change->pre_change_function(x, y);
10453   }
10454
10455   ChangeDelay[x][y]--;
10456
10457   if (ChangeDelay[x][y] != 0)           /* continue element change */
10458   {
10459     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10460
10461     if (IS_ANIMATED(graphic))
10462       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10463
10464     if (change->change_function)
10465       change->change_function(x, y);
10466   }
10467   else                                  /* finish element change */
10468   {
10469     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10470     {
10471       page = ChangePage[x][y];
10472       ChangePage[x][y] = -1;
10473
10474       change = &ei->change_page[page];
10475     }
10476
10477     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10478     {
10479       ChangeDelay[x][y] = 1;            /* try change after next move step */
10480       ChangePage[x][y] = page;          /* remember page to use for change */
10481
10482       return;
10483     }
10484
10485     if (ChangeElement(x, y, element, page))
10486     {
10487       if (change->post_change_function)
10488         change->post_change_function(x, y);
10489     }
10490   }
10491 }
10492
10493 #endif
10494
10495 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10496                                               int trigger_element,
10497                                               int trigger_event,
10498                                               int trigger_player,
10499                                               int trigger_side,
10500                                               int trigger_page)
10501 {
10502   boolean change_done_any = FALSE;
10503   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10504   int i;
10505
10506   if (!(trigger_events[trigger_element][trigger_event]))
10507     return FALSE;
10508
10509 #if 0
10510   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10511          trigger_event, recursion_loop_depth, recursion_loop_detected,
10512          recursion_loop_element, EL_NAME(recursion_loop_element));
10513 #endif
10514
10515   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10516
10517   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10518   {
10519     int element = EL_CUSTOM_START + i;
10520     boolean change_done = FALSE;
10521     int p;
10522
10523     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10524         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10525       continue;
10526
10527     for (p = 0; p < element_info[element].num_change_pages; p++)
10528     {
10529       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10530
10531       if (change->can_change_or_has_action &&
10532           change->has_event[trigger_event] &&
10533           change->trigger_side & trigger_side &&
10534           change->trigger_player & trigger_player &&
10535           change->trigger_page & trigger_page_bits &&
10536           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10537       {
10538         change->actual_trigger_element = trigger_element;
10539         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10540         change->actual_trigger_side = trigger_side;
10541         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10542         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10543
10544         if ((change->can_change && !change_done) || change->has_action)
10545         {
10546           int x, y;
10547
10548           SCAN_PLAYFIELD(x, y)
10549           {
10550             if (Feld[x][y] == element)
10551             {
10552               if (change->can_change && !change_done)
10553               {
10554                 ChangeDelay[x][y] = 1;
10555                 ChangeEvent[x][y] = trigger_event;
10556
10557                 HandleElementChange(x, y, p);
10558               }
10559 #if USE_NEW_DELAYED_ACTION
10560               else if (change->has_action)
10561               {
10562                 ExecuteCustomElementAction(x, y, element, p);
10563                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10564               }
10565 #else
10566               if (change->has_action)
10567               {
10568                 ExecuteCustomElementAction(x, y, element, p);
10569                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10570               }
10571 #endif
10572             }
10573           }
10574
10575           if (change->can_change)
10576           {
10577             change_done = TRUE;
10578             change_done_any = TRUE;
10579           }
10580         }
10581       }
10582     }
10583   }
10584
10585   RECURSION_LOOP_DETECTION_END();
10586
10587   return change_done_any;
10588 }
10589
10590 static boolean CheckElementChangeExt(int x, int y,
10591                                      int element,
10592                                      int trigger_element,
10593                                      int trigger_event,
10594                                      int trigger_player,
10595                                      int trigger_side)
10596 {
10597   boolean change_done = FALSE;
10598   int p;
10599
10600   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10601       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10602     return FALSE;
10603
10604   if (Feld[x][y] == EL_BLOCKED)
10605   {
10606     Blocked2Moving(x, y, &x, &y);
10607     element = Feld[x][y];
10608   }
10609
10610 #if 0
10611   /* check if element has already changed */
10612   if (Feld[x][y] != element)
10613     return FALSE;
10614 #else
10615   /* check if element has already changed or is about to change after moving */
10616   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10617        Feld[x][y] != element) ||
10618
10619       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10620        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10621         ChangePage[x][y] != -1)))
10622     return FALSE;
10623 #endif
10624
10625 #if 0
10626   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10627          trigger_event, recursion_loop_depth, recursion_loop_detected,
10628          recursion_loop_element, EL_NAME(recursion_loop_element));
10629 #endif
10630
10631   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10632
10633   for (p = 0; p < element_info[element].num_change_pages; p++)
10634   {
10635     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10636
10637     /* check trigger element for all events where the element that is checked
10638        for changing interacts with a directly adjacent element -- this is
10639        different to element changes that affect other elements to change on the
10640        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10641     boolean check_trigger_element =
10642       (trigger_event == CE_TOUCHING_X ||
10643        trigger_event == CE_HITTING_X ||
10644        trigger_event == CE_HIT_BY_X ||
10645 #if 1
10646        /* this one was forgotten until 3.2.3 */
10647        trigger_event == CE_DIGGING_X);
10648 #endif
10649
10650     if (change->can_change_or_has_action &&
10651         change->has_event[trigger_event] &&
10652         change->trigger_side & trigger_side &&
10653         change->trigger_player & trigger_player &&
10654         (!check_trigger_element ||
10655          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10656     {
10657       change->actual_trigger_element = trigger_element;
10658       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10659       change->actual_trigger_side = trigger_side;
10660       change->actual_trigger_ce_value = CustomValue[x][y];
10661       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10662
10663       /* special case: trigger element not at (x,y) position for some events */
10664       if (check_trigger_element)
10665       {
10666         static struct
10667         {
10668           int dx, dy;
10669         } move_xy[] =
10670           {
10671             {  0,  0 },
10672             { -1,  0 },
10673             { +1,  0 },
10674             {  0,  0 },
10675             {  0, -1 },
10676             {  0,  0 }, { 0, 0 }, { 0, 0 },
10677             {  0, +1 }
10678           };
10679
10680         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10681         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10682
10683         change->actual_trigger_ce_value = CustomValue[xx][yy];
10684         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10685       }
10686
10687       if (change->can_change && !change_done)
10688       {
10689         ChangeDelay[x][y] = 1;
10690         ChangeEvent[x][y] = trigger_event;
10691
10692         HandleElementChange(x, y, p);
10693
10694         change_done = TRUE;
10695       }
10696 #if USE_NEW_DELAYED_ACTION
10697       else if (change->has_action)
10698       {
10699         ExecuteCustomElementAction(x, y, element, p);
10700         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10701       }
10702 #else
10703       if (change->has_action)
10704       {
10705         ExecuteCustomElementAction(x, y, element, p);
10706         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10707       }
10708 #endif
10709     }
10710   }
10711
10712   RECURSION_LOOP_DETECTION_END();
10713
10714   return change_done;
10715 }
10716
10717 static void PlayPlayerSound(struct PlayerInfo *player)
10718 {
10719   int jx = player->jx, jy = player->jy;
10720   int sound_element = player->artwork_element;
10721   int last_action = player->last_action_waiting;
10722   int action = player->action_waiting;
10723
10724   if (player->is_waiting)
10725   {
10726     if (action != last_action)
10727       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10728     else
10729       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10730   }
10731   else
10732   {
10733     if (action != last_action)
10734       StopSound(element_info[sound_element].sound[last_action]);
10735
10736     if (last_action == ACTION_SLEEPING)
10737       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10738   }
10739 }
10740
10741 static void PlayAllPlayersSound()
10742 {
10743   int i;
10744
10745   for (i = 0; i < MAX_PLAYERS; i++)
10746     if (stored_player[i].active)
10747       PlayPlayerSound(&stored_player[i]);
10748 }
10749
10750 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10751 {
10752   boolean last_waiting = player->is_waiting;
10753   int move_dir = player->MovDir;
10754
10755   player->dir_waiting = move_dir;
10756   player->last_action_waiting = player->action_waiting;
10757
10758   if (is_waiting)
10759   {
10760     if (!last_waiting)          /* not waiting -> waiting */
10761     {
10762       player->is_waiting = TRUE;
10763
10764       player->frame_counter_bored =
10765         FrameCounter +
10766         game.player_boring_delay_fixed +
10767         GetSimpleRandom(game.player_boring_delay_random);
10768       player->frame_counter_sleeping =
10769         FrameCounter +
10770         game.player_sleeping_delay_fixed +
10771         GetSimpleRandom(game.player_sleeping_delay_random);
10772
10773       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10774     }
10775
10776     if (game.player_sleeping_delay_fixed +
10777         game.player_sleeping_delay_random > 0 &&
10778         player->anim_delay_counter == 0 &&
10779         player->post_delay_counter == 0 &&
10780         FrameCounter >= player->frame_counter_sleeping)
10781       player->is_sleeping = TRUE;
10782     else if (game.player_boring_delay_fixed +
10783              game.player_boring_delay_random > 0 &&
10784              FrameCounter >= player->frame_counter_bored)
10785       player->is_bored = TRUE;
10786
10787     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10788                               player->is_bored ? ACTION_BORING :
10789                               ACTION_WAITING);
10790
10791     if (player->is_sleeping && player->use_murphy)
10792     {
10793       /* special case for sleeping Murphy when leaning against non-free tile */
10794
10795       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10796           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10797            !IS_MOVING(player->jx - 1, player->jy)))
10798         move_dir = MV_LEFT;
10799       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10800                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10801                 !IS_MOVING(player->jx + 1, player->jy)))
10802         move_dir = MV_RIGHT;
10803       else
10804         player->is_sleeping = FALSE;
10805
10806       player->dir_waiting = move_dir;
10807     }
10808
10809     if (player->is_sleeping)
10810     {
10811       if (player->num_special_action_sleeping > 0)
10812       {
10813         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10814         {
10815           int last_special_action = player->special_action_sleeping;
10816           int num_special_action = player->num_special_action_sleeping;
10817           int special_action =
10818             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10819              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10820              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10821              last_special_action + 1 : ACTION_SLEEPING);
10822           int special_graphic =
10823             el_act_dir2img(player->artwork_element, special_action, move_dir);
10824
10825           player->anim_delay_counter =
10826             graphic_info[special_graphic].anim_delay_fixed +
10827             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10828           player->post_delay_counter =
10829             graphic_info[special_graphic].post_delay_fixed +
10830             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10831
10832           player->special_action_sleeping = special_action;
10833         }
10834
10835         if (player->anim_delay_counter > 0)
10836         {
10837           player->action_waiting = player->special_action_sleeping;
10838           player->anim_delay_counter--;
10839         }
10840         else if (player->post_delay_counter > 0)
10841         {
10842           player->post_delay_counter--;
10843         }
10844       }
10845     }
10846     else if (player->is_bored)
10847     {
10848       if (player->num_special_action_bored > 0)
10849       {
10850         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10851         {
10852           int special_action =
10853             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10854           int special_graphic =
10855             el_act_dir2img(player->artwork_element, special_action, move_dir);
10856
10857           player->anim_delay_counter =
10858             graphic_info[special_graphic].anim_delay_fixed +
10859             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10860           player->post_delay_counter =
10861             graphic_info[special_graphic].post_delay_fixed +
10862             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10863
10864           player->special_action_bored = special_action;
10865         }
10866
10867         if (player->anim_delay_counter > 0)
10868         {
10869           player->action_waiting = player->special_action_bored;
10870           player->anim_delay_counter--;
10871         }
10872         else if (player->post_delay_counter > 0)
10873         {
10874           player->post_delay_counter--;
10875         }
10876       }
10877     }
10878   }
10879   else if (last_waiting)        /* waiting -> not waiting */
10880   {
10881     player->is_waiting = FALSE;
10882     player->is_bored = FALSE;
10883     player->is_sleeping = FALSE;
10884
10885     player->frame_counter_bored = -1;
10886     player->frame_counter_sleeping = -1;
10887
10888     player->anim_delay_counter = 0;
10889     player->post_delay_counter = 0;
10890
10891     player->dir_waiting = player->MovDir;
10892     player->action_waiting = ACTION_DEFAULT;
10893
10894     player->special_action_bored = ACTION_DEFAULT;
10895     player->special_action_sleeping = ACTION_DEFAULT;
10896   }
10897 }
10898
10899 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10900 {
10901   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
10902   int left      = player_action & JOY_LEFT;
10903   int right     = player_action & JOY_RIGHT;
10904   int up        = player_action & JOY_UP;
10905   int down      = player_action & JOY_DOWN;
10906   int button1   = player_action & JOY_BUTTON_1;
10907   int button2   = player_action & JOY_BUTTON_2;
10908   int dx        = (left ? -1 : right ? 1 : 0);
10909   int dy        = (up   ? -1 : down  ? 1 : 0);
10910
10911   if (!player->active || tape.pausing)
10912     return 0;
10913
10914   if (player_action)
10915   {
10916     if (button1)
10917       snapped = SnapField(player, dx, dy);
10918     else
10919     {
10920       if (button2)
10921         dropped = DropElement(player);
10922
10923       moved = MovePlayer(player, dx, dy);
10924     }
10925
10926     if (tape.single_step && tape.recording && !tape.pausing)
10927     {
10928       if (button1 || (dropped && !moved))
10929       {
10930         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10931         SnapField(player, 0, 0);                /* stop snapping */
10932       }
10933     }
10934
10935     SetPlayerWaiting(player, FALSE);
10936
10937     return player_action;
10938   }
10939   else
10940   {
10941     /* no actions for this player (no input at player's configured device) */
10942
10943     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10944     SnapField(player, 0, 0);
10945     CheckGravityMovementWhenNotMoving(player);
10946
10947     if (player->MovPos == 0)
10948       SetPlayerWaiting(player, TRUE);
10949
10950     if (player->MovPos == 0)    /* needed for tape.playing */
10951       player->is_moving = FALSE;
10952
10953     player->is_dropping = FALSE;
10954     player->is_dropping_pressed = FALSE;
10955     player->drop_pressed_delay = 0;
10956
10957     return 0;
10958   }
10959 }
10960
10961 static void CheckLevelTime()
10962 {
10963   int i;
10964
10965   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10966   {
10967     if (level.native_em_level->lev->home == 0)  /* all players at home */
10968     {
10969       PlayerWins(local_player);
10970
10971       AllPlayersGone = TRUE;
10972
10973       level.native_em_level->lev->home = -1;
10974     }
10975
10976     if (level.native_em_level->ply[0]->alive == 0 &&
10977         level.native_em_level->ply[1]->alive == 0 &&
10978         level.native_em_level->ply[2]->alive == 0 &&
10979         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10980       AllPlayersGone = TRUE;
10981   }
10982
10983   if (TimeFrames >= FRAMES_PER_SECOND)
10984   {
10985     TimeFrames = 0;
10986     TapeTime++;
10987
10988     for (i = 0; i < MAX_PLAYERS; i++)
10989     {
10990       struct PlayerInfo *player = &stored_player[i];
10991
10992       if (SHIELD_ON(player))
10993       {
10994         player->shield_normal_time_left--;
10995
10996         if (player->shield_deadly_time_left > 0)
10997           player->shield_deadly_time_left--;
10998       }
10999     }
11000
11001     if (!local_player->LevelSolved && !level.use_step_counter)
11002     {
11003       TimePlayed++;
11004
11005       if (TimeLeft > 0)
11006       {
11007         TimeLeft--;
11008
11009         if (TimeLeft <= 10 && setup.time_limit)
11010           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11011
11012 #if 1
11013         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11014
11015         DisplayGameControlValues();
11016 #else
11017         DrawGameValue_Time(TimeLeft);
11018 #endif
11019
11020         if (!TimeLeft && setup.time_limit)
11021         {
11022           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11023             level.native_em_level->lev->killed_out_of_time = TRUE;
11024           else
11025             for (i = 0; i < MAX_PLAYERS; i++)
11026               KillPlayer(&stored_player[i]);
11027         }
11028       }
11029 #if 1
11030       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11031       {
11032         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11033
11034         DisplayGameControlValues();
11035       }
11036 #else
11037       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11038         DrawGameValue_Time(TimePlayed);
11039 #endif
11040
11041       level.native_em_level->lev->time =
11042         (level.time == 0 ? TimePlayed : TimeLeft);
11043     }
11044
11045     if (tape.recording || tape.playing)
11046       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11047   }
11048
11049   UpdateGameDoorValues();
11050   DrawGameDoorValues();
11051 }
11052
11053 void AdvanceFrameAndPlayerCounters(int player_nr)
11054 {
11055   int i;
11056
11057   /* advance frame counters (global frame counter and time frame counter) */
11058   FrameCounter++;
11059   TimeFrames++;
11060
11061   /* advance player counters (counters for move delay, move animation etc.) */
11062   for (i = 0; i < MAX_PLAYERS; i++)
11063   {
11064     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11065     int move_delay_value = stored_player[i].move_delay_value;
11066     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11067
11068     if (!advance_player_counters)       /* not all players may be affected */
11069       continue;
11070
11071 #if USE_NEW_PLAYER_ANIM
11072     if (move_frames == 0)       /* less than one move per game frame */
11073     {
11074       int stepsize = TILEX / move_delay_value;
11075       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11076       int count = (stored_player[i].is_moving ?
11077                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11078
11079       if (count % delay == 0)
11080         move_frames = 1;
11081     }
11082 #endif
11083
11084     stored_player[i].Frame += move_frames;
11085
11086     if (stored_player[i].MovPos != 0)
11087       stored_player[i].StepFrame += move_frames;
11088
11089     if (stored_player[i].move_delay > 0)
11090       stored_player[i].move_delay--;
11091
11092     /* due to bugs in previous versions, counter must count up, not down */
11093     if (stored_player[i].push_delay != -1)
11094       stored_player[i].push_delay++;
11095
11096     if (stored_player[i].drop_delay > 0)
11097       stored_player[i].drop_delay--;
11098
11099     if (stored_player[i].is_dropping_pressed)
11100       stored_player[i].drop_pressed_delay++;
11101   }
11102 }
11103
11104 void StartGameActions(boolean init_network_game, boolean record_tape,
11105                       long random_seed)
11106 {
11107   unsigned long new_random_seed = InitRND(random_seed);
11108
11109   if (record_tape)
11110     TapeStartRecording(new_random_seed);
11111
11112 #if defined(NETWORK_AVALIABLE)
11113   if (init_network_game)
11114   {
11115     SendToServer_StartPlaying();
11116
11117     return;
11118   }
11119 #endif
11120
11121   InitGame();
11122 }
11123
11124 void GameActions()
11125 {
11126   static unsigned long game_frame_delay = 0;
11127   unsigned long game_frame_delay_value;
11128   byte *recorded_player_action;
11129   byte summarized_player_action = 0;
11130   byte tape_action[MAX_PLAYERS];
11131   int i;
11132
11133   /* detect endless loops, caused by custom element programming */
11134   if (recursion_loop_detected && recursion_loop_depth == 0)
11135   {
11136     char *message = getStringCat3("Internal Error ! Element ",
11137                                   EL_NAME(recursion_loop_element),
11138                                   " caused endless loop ! Quit the game ?");
11139
11140     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11141           EL_NAME(recursion_loop_element));
11142
11143     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11144
11145     recursion_loop_detected = FALSE;    /* if game should be continued */
11146
11147     free(message);
11148
11149     return;
11150   }
11151
11152   if (game.restart_level)
11153     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11154
11155   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11156   {
11157     if (level.native_em_level->lev->home == 0)  /* all players at home */
11158     {
11159       PlayerWins(local_player);
11160
11161       AllPlayersGone = TRUE;
11162
11163       level.native_em_level->lev->home = -1;
11164     }
11165
11166     if (level.native_em_level->ply[0]->alive == 0 &&
11167         level.native_em_level->ply[1]->alive == 0 &&
11168         level.native_em_level->ply[2]->alive == 0 &&
11169         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11170       AllPlayersGone = TRUE;
11171   }
11172
11173   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11174     GameWon();
11175
11176   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11177     TapeStop();
11178
11179   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11180     return;
11181
11182   game_frame_delay_value =
11183     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11184
11185   if (tape.playing && tape.warp_forward && !tape.pausing)
11186     game_frame_delay_value = 0;
11187
11188   /* ---------- main game synchronization point ---------- */
11189
11190   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11191
11192   if (network_playing && !network_player_action_received)
11193   {
11194     /* try to get network player actions in time */
11195
11196 #if defined(NETWORK_AVALIABLE)
11197     /* last chance to get network player actions without main loop delay */
11198     HandleNetworking();
11199 #endif
11200
11201     /* game was quit by network peer */
11202     if (game_status != GAME_MODE_PLAYING)
11203       return;
11204
11205     if (!network_player_action_received)
11206       return;           /* failed to get network player actions in time */
11207
11208     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11209   }
11210
11211   if (tape.pausing)
11212     return;
11213
11214   /* at this point we know that we really continue executing the game */
11215
11216   network_player_action_received = FALSE;
11217
11218   /* when playing tape, read previously recorded player input from tape data */
11219   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11220
11221 #if 1
11222   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11223   if (tape.pausing)
11224     return;
11225 #endif
11226
11227   if (tape.set_centered_player)
11228   {
11229     game.centered_player_nr_next = tape.centered_player_nr_next;
11230     game.set_centered_player = TRUE;
11231   }
11232
11233   for (i = 0; i < MAX_PLAYERS; i++)
11234   {
11235     summarized_player_action |= stored_player[i].action;
11236
11237     if (!network_playing)
11238       stored_player[i].effective_action = stored_player[i].action;
11239   }
11240
11241 #if defined(NETWORK_AVALIABLE)
11242   if (network_playing)
11243     SendToServer_MovePlayer(summarized_player_action);
11244 #endif
11245
11246   if (!options.network && !setup.team_mode)
11247     local_player->effective_action = summarized_player_action;
11248
11249   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11250   {
11251     for (i = 0; i < MAX_PLAYERS; i++)
11252       stored_player[i].effective_action =
11253         (i == game.centered_player_nr ? summarized_player_action : 0);
11254   }
11255
11256   if (recorded_player_action != NULL)
11257     for (i = 0; i < MAX_PLAYERS; i++)
11258       stored_player[i].effective_action = recorded_player_action[i];
11259
11260   for (i = 0; i < MAX_PLAYERS; i++)
11261   {
11262     tape_action[i] = stored_player[i].effective_action;
11263
11264     /* (this can only happen in the R'n'D game engine) */
11265     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11266       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11267   }
11268
11269   /* only record actions from input devices, but not programmed actions */
11270   if (tape.recording)
11271     TapeRecordAction(tape_action);
11272
11273   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11274   {
11275     GameActions_EM_Main();
11276   }
11277   else
11278   {
11279     GameActions_RND();
11280   }
11281 }
11282
11283 void GameActions_EM_Main()
11284 {
11285   byte effective_action[MAX_PLAYERS];
11286   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11287   int i;
11288
11289   for (i = 0; i < MAX_PLAYERS; i++)
11290     effective_action[i] = stored_player[i].effective_action;
11291
11292   GameActions_EM(effective_action, warp_mode);
11293
11294   CheckLevelTime();
11295
11296   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11297 }
11298
11299 void GameActions_RND()
11300 {
11301   int magic_wall_x = 0, magic_wall_y = 0;
11302   int i, x, y, element, graphic;
11303
11304   InitPlayfieldScanModeVars();
11305
11306 #if USE_ONE_MORE_CHANGE_PER_FRAME
11307   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11308   {
11309     SCAN_PLAYFIELD(x, y)
11310     {
11311       ChangeCount[x][y] = 0;
11312       ChangeEvent[x][y] = -1;
11313     }
11314   }
11315 #endif
11316
11317   if (game.set_centered_player)
11318   {
11319     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11320
11321     /* switching to "all players" only possible if all players fit to screen */
11322     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11323     {
11324       game.centered_player_nr_next = game.centered_player_nr;
11325       game.set_centered_player = FALSE;
11326     }
11327
11328     /* do not switch focus to non-existing (or non-active) player */
11329     if (game.centered_player_nr_next >= 0 &&
11330         !stored_player[game.centered_player_nr_next].active)
11331     {
11332       game.centered_player_nr_next = game.centered_player_nr;
11333       game.set_centered_player = FALSE;
11334     }
11335   }
11336
11337   if (game.set_centered_player &&
11338       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11339   {
11340     int sx, sy;
11341
11342     if (game.centered_player_nr_next == -1)
11343     {
11344       setScreenCenteredToAllPlayers(&sx, &sy);
11345     }
11346     else
11347     {
11348       sx = stored_player[game.centered_player_nr_next].jx;
11349       sy = stored_player[game.centered_player_nr_next].jy;
11350     }
11351
11352     game.centered_player_nr = game.centered_player_nr_next;
11353     game.set_centered_player = FALSE;
11354
11355     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11356     DrawGameDoorValues();
11357   }
11358
11359   for (i = 0; i < MAX_PLAYERS; i++)
11360   {
11361     int actual_player_action = stored_player[i].effective_action;
11362
11363 #if 1
11364     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11365        - rnd_equinox_tetrachloride 048
11366        - rnd_equinox_tetrachloride_ii 096
11367        - rnd_emanuel_schmieg 002
11368        - doctor_sloan_ww 001, 020
11369     */
11370     if (stored_player[i].MovPos == 0)
11371       CheckGravityMovement(&stored_player[i]);
11372 #endif
11373
11374     /* overwrite programmed action with tape action */
11375     if (stored_player[i].programmed_action)
11376       actual_player_action = stored_player[i].programmed_action;
11377
11378     PlayerActions(&stored_player[i], actual_player_action);
11379
11380     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11381   }
11382
11383   ScrollScreen(NULL, SCROLL_GO_ON);
11384
11385   /* for backwards compatibility, the following code emulates a fixed bug that
11386      occured when pushing elements (causing elements that just made their last
11387      pushing step to already (if possible) make their first falling step in the
11388      same game frame, which is bad); this code is also needed to use the famous
11389      "spring push bug" which is used in older levels and might be wanted to be
11390      used also in newer levels, but in this case the buggy pushing code is only
11391      affecting the "spring" element and no other elements */
11392
11393   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11394   {
11395     for (i = 0; i < MAX_PLAYERS; i++)
11396     {
11397       struct PlayerInfo *player = &stored_player[i];
11398       int x = player->jx;
11399       int y = player->jy;
11400
11401       if (player->active && player->is_pushing && player->is_moving &&
11402           IS_MOVING(x, y) &&
11403           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11404            Feld[x][y] == EL_SPRING))
11405       {
11406         ContinueMoving(x, y);
11407
11408         /* continue moving after pushing (this is actually a bug) */
11409         if (!IS_MOVING(x, y))
11410           Stop[x][y] = FALSE;
11411       }
11412     }
11413   }
11414
11415 #if 0
11416   debug_print_timestamp(0, "start main loop profiling");
11417 #endif
11418
11419   SCAN_PLAYFIELD(x, y)
11420   {
11421     ChangeCount[x][y] = 0;
11422     ChangeEvent[x][y] = -1;
11423
11424     /* this must be handled before main playfield loop */
11425     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11426     {
11427       MovDelay[x][y]--;
11428       if (MovDelay[x][y] <= 0)
11429         RemoveField(x, y);
11430     }
11431
11432 #if USE_NEW_SNAP_DELAY
11433     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11434     {
11435       MovDelay[x][y]--;
11436       if (MovDelay[x][y] <= 0)
11437       {
11438         RemoveField(x, y);
11439         DrawLevelField(x, y);
11440
11441         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11442       }
11443     }
11444 #endif
11445
11446 #if DEBUG
11447     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11448     {
11449       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11450       printf("GameActions(): This should never happen!\n");
11451
11452       ChangePage[x][y] = -1;
11453     }
11454 #endif
11455
11456     Stop[x][y] = FALSE;
11457     if (WasJustMoving[x][y] > 0)
11458       WasJustMoving[x][y]--;
11459     if (WasJustFalling[x][y] > 0)
11460       WasJustFalling[x][y]--;
11461     if (CheckCollision[x][y] > 0)
11462       CheckCollision[x][y]--;
11463     if (CheckImpact[x][y] > 0)
11464       CheckImpact[x][y]--;
11465
11466     GfxFrame[x][y]++;
11467
11468     /* reset finished pushing action (not done in ContinueMoving() to allow
11469        continuous pushing animation for elements with zero push delay) */
11470     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11471     {
11472       ResetGfxAnimation(x, y);
11473       DrawLevelField(x, y);
11474     }
11475
11476 #if DEBUG
11477     if (IS_BLOCKED(x, y))
11478     {
11479       int oldx, oldy;
11480
11481       Blocked2Moving(x, y, &oldx, &oldy);
11482       if (!IS_MOVING(oldx, oldy))
11483       {
11484         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11485         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11486         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11487         printf("GameActions(): This should never happen!\n");
11488       }
11489     }
11490 #endif
11491   }
11492
11493 #if 0
11494   debug_print_timestamp(0, "- time for pre-main loop:");
11495 #endif
11496
11497 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11498   SCAN_PLAYFIELD(x, y)
11499   {
11500     element = Feld[x][y];
11501     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11502
11503 #if 1
11504     {
11505 #if 1
11506       int element2 = element;
11507       int graphic2 = graphic;
11508 #else
11509       int element2 = Feld[x][y];
11510       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11511 #endif
11512       int last_gfx_frame = GfxFrame[x][y];
11513
11514       if (graphic_info[graphic2].anim_global_sync)
11515         GfxFrame[x][y] = FrameCounter;
11516       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11517         GfxFrame[x][y] = CustomValue[x][y];
11518       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11519         GfxFrame[x][y] = element_info[element2].collect_score;
11520       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11521         GfxFrame[x][y] = ChangeDelay[x][y];
11522
11523       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11524         DrawLevelGraphicAnimation(x, y, graphic2);
11525     }
11526 #else
11527     ResetGfxFrame(x, y, TRUE);
11528 #endif
11529
11530 #if 1
11531     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11532         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11533       ResetRandomAnimationValue(x, y);
11534 #endif
11535
11536 #if 1
11537     SetRandomAnimationValue(x, y);
11538 #endif
11539
11540 #if 1
11541     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11542 #endif
11543   }
11544 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11545
11546 #if 0
11547   debug_print_timestamp(0, "- time for TEST loop:     -->");
11548 #endif
11549
11550   SCAN_PLAYFIELD(x, y)
11551   {
11552     element = Feld[x][y];
11553     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11554
11555     ResetGfxFrame(x, y, TRUE);
11556
11557     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11558         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11559       ResetRandomAnimationValue(x, y);
11560
11561     SetRandomAnimationValue(x, y);
11562
11563     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11564
11565     if (IS_INACTIVE(element))
11566     {
11567       if (IS_ANIMATED(graphic))
11568         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11569
11570       continue;
11571     }
11572
11573     /* this may take place after moving, so 'element' may have changed */
11574     if (IS_CHANGING(x, y) &&
11575         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11576     {
11577       int page = element_info[element].event_page_nr[CE_DELAY];
11578
11579 #if 1
11580       HandleElementChange(x, y, page);
11581 #else
11582       if (CAN_CHANGE(element))
11583         HandleElementChange(x, y, page);
11584
11585       if (HAS_ACTION(element))
11586         ExecuteCustomElementAction(x, y, element, page);
11587 #endif
11588
11589       element = Feld[x][y];
11590       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11591     }
11592
11593 #if 0   // ---------------------------------------------------------------------
11594
11595     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11596     {
11597       StartMoving(x, y);
11598
11599       element = Feld[x][y];
11600       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11601
11602       if (IS_ANIMATED(graphic) &&
11603           !IS_MOVING(x, y) &&
11604           !Stop[x][y])
11605         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11606
11607       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11608         DrawTwinkleOnField(x, y);
11609     }
11610     else if (IS_MOVING(x, y))
11611       ContinueMoving(x, y);
11612     else
11613     {
11614       switch (element)
11615       {
11616         case EL_ACID:
11617         case EL_EXIT_OPEN:
11618         case EL_EM_EXIT_OPEN:
11619         case EL_SP_EXIT_OPEN:
11620         case EL_STEEL_EXIT_OPEN:
11621         case EL_EM_STEEL_EXIT_OPEN:
11622         case EL_SP_TERMINAL:
11623         case EL_SP_TERMINAL_ACTIVE:
11624         case EL_EXTRA_TIME:
11625         case EL_SHIELD_NORMAL:
11626         case EL_SHIELD_DEADLY:
11627           if (IS_ANIMATED(graphic))
11628             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11629           break;
11630
11631         case EL_DYNAMITE_ACTIVE:
11632         case EL_EM_DYNAMITE_ACTIVE:
11633         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11634         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11635         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11636         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11637         case EL_SP_DISK_RED_ACTIVE:
11638           CheckDynamite(x, y);
11639           break;
11640
11641         case EL_AMOEBA_GROWING:
11642           AmoebeWaechst(x, y);
11643           break;
11644
11645         case EL_AMOEBA_SHRINKING:
11646           AmoebaDisappearing(x, y);
11647           break;
11648
11649 #if !USE_NEW_AMOEBA_CODE
11650         case EL_AMOEBA_WET:
11651         case EL_AMOEBA_DRY:
11652         case EL_AMOEBA_FULL:
11653         case EL_BD_AMOEBA:
11654         case EL_EMC_DRIPPER:
11655           AmoebeAbleger(x, y);
11656           break;
11657 #endif
11658
11659         case EL_GAME_OF_LIFE:
11660         case EL_BIOMAZE:
11661           Life(x, y);
11662           break;
11663
11664         case EL_EXIT_CLOSED:
11665           CheckExit(x, y);
11666           break;
11667
11668         case EL_EM_EXIT_CLOSED:
11669           CheckExitEM(x, y);
11670           break;
11671
11672         case EL_STEEL_EXIT_CLOSED:
11673           CheckExitSteel(x, y);
11674           break;
11675
11676         case EL_EM_STEEL_EXIT_CLOSED:
11677           CheckExitSteelEM(x, y);
11678           break;
11679
11680         case EL_SP_EXIT_CLOSED:
11681           CheckExitSP(x, y);
11682           break;
11683
11684         case EL_EXPANDABLE_WALL_GROWING:
11685         case EL_EXPANDABLE_STEELWALL_GROWING:
11686           MauerWaechst(x, y);
11687           break;
11688
11689         case EL_EXPANDABLE_WALL:
11690         case EL_EXPANDABLE_WALL_HORIZONTAL:
11691         case EL_EXPANDABLE_WALL_VERTICAL:
11692         case EL_EXPANDABLE_WALL_ANY:
11693         case EL_BD_EXPANDABLE_WALL:
11694           MauerAbleger(x, y);
11695           break;
11696
11697         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
11698         case EL_EXPANDABLE_STEELWALL_VERTICAL:
11699         case EL_EXPANDABLE_STEELWALL_ANY:
11700           MauerAblegerStahl(x, y);
11701           break;
11702
11703         case EL_FLAMES:
11704           CheckForDragon(x, y);
11705           break;
11706
11707         case EL_EXPLOSION:
11708           break;
11709
11710         case EL_ELEMENT_SNAPPING:
11711         case EL_DIAGONAL_SHRINKING:
11712         case EL_DIAGONAL_GROWING:
11713         {
11714           graphic =
11715             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11716
11717           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11718           break;
11719         }
11720
11721         default:
11722           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11723             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11724           break;
11725       }
11726     }
11727
11728 #else   // ---------------------------------------------------------------------
11729
11730     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11731     {
11732       StartMoving(x, y);
11733
11734       element = Feld[x][y];
11735       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11736
11737       if (IS_ANIMATED(graphic) &&
11738           !IS_MOVING(x, y) &&
11739           !Stop[x][y])
11740         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11741
11742       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11743         DrawTwinkleOnField(x, y);
11744     }
11745     else if ((element == EL_ACID ||
11746               element == EL_EXIT_OPEN ||
11747               element == EL_EM_EXIT_OPEN ||
11748               element == EL_SP_EXIT_OPEN ||
11749               element == EL_STEEL_EXIT_OPEN ||
11750               element == EL_EM_STEEL_EXIT_OPEN ||
11751               element == EL_SP_TERMINAL ||
11752               element == EL_SP_TERMINAL_ACTIVE ||
11753               element == EL_EXTRA_TIME ||
11754               element == EL_SHIELD_NORMAL ||
11755               element == EL_SHIELD_DEADLY) &&
11756              IS_ANIMATED(graphic))
11757       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11758     else if (IS_MOVING(x, y))
11759       ContinueMoving(x, y);
11760     else if (IS_ACTIVE_BOMB(element))
11761       CheckDynamite(x, y);
11762     else if (element == EL_AMOEBA_GROWING)
11763       AmoebeWaechst(x, y);
11764     else if (element == EL_AMOEBA_SHRINKING)
11765       AmoebaDisappearing(x, y);
11766
11767 #if !USE_NEW_AMOEBA_CODE
11768     else if (IS_AMOEBALIVE(element))
11769       AmoebeAbleger(x, y);
11770 #endif
11771
11772     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11773       Life(x, y);
11774     else if (element == EL_EXIT_CLOSED)
11775       CheckExit(x, y);
11776     else if (element == EL_EM_EXIT_CLOSED)
11777       CheckExitEM(x, y);
11778     else if (element == EL_STEEL_EXIT_CLOSED)
11779       CheckExitSteel(x, y);
11780     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11781       CheckExitSteelEM(x, y);
11782     else if (element == EL_SP_EXIT_CLOSED)
11783       CheckExitSP(x, y);
11784     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11785              element == EL_EXPANDABLE_STEELWALL_GROWING)
11786       MauerWaechst(x, y);
11787     else if (element == EL_EXPANDABLE_WALL ||
11788              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11789              element == EL_EXPANDABLE_WALL_VERTICAL ||
11790              element == EL_EXPANDABLE_WALL_ANY ||
11791              element == EL_BD_EXPANDABLE_WALL)
11792       MauerAbleger(x, y);
11793     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11794              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11795              element == EL_EXPANDABLE_STEELWALL_ANY)
11796       MauerAblegerStahl(x, y);
11797     else if (element == EL_FLAMES)
11798       CheckForDragon(x, y);
11799     else if (element == EL_EXPLOSION)
11800       ; /* drawing of correct explosion animation is handled separately */
11801     else if (element == EL_ELEMENT_SNAPPING ||
11802              element == EL_DIAGONAL_SHRINKING ||
11803              element == EL_DIAGONAL_GROWING)
11804     {
11805       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11806
11807       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11808     }
11809     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11810       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11811
11812 #endif  // ---------------------------------------------------------------------
11813
11814     if (IS_BELT_ACTIVE(element))
11815       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11816
11817     if (game.magic_wall_active)
11818     {
11819       int jx = local_player->jx, jy = local_player->jy;
11820
11821       /* play the element sound at the position nearest to the player */
11822       if ((element == EL_MAGIC_WALL_FULL ||
11823            element == EL_MAGIC_WALL_ACTIVE ||
11824            element == EL_MAGIC_WALL_EMPTYING ||
11825            element == EL_BD_MAGIC_WALL_FULL ||
11826            element == EL_BD_MAGIC_WALL_ACTIVE ||
11827            element == EL_BD_MAGIC_WALL_EMPTYING ||
11828            element == EL_DC_MAGIC_WALL_FULL ||
11829            element == EL_DC_MAGIC_WALL_ACTIVE ||
11830            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11831           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11832       {
11833         magic_wall_x = x;
11834         magic_wall_y = y;
11835       }
11836     }
11837   }
11838
11839 #if 0
11840   debug_print_timestamp(0, "- time for MAIN loop:     -->");
11841 #endif
11842
11843 #if USE_NEW_AMOEBA_CODE
11844   /* new experimental amoeba growth stuff */
11845   if (!(FrameCounter % 8))
11846   {
11847     static unsigned long random = 1684108901;
11848
11849     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11850     {
11851       x = RND(lev_fieldx);
11852       y = RND(lev_fieldy);
11853       element = Feld[x][y];
11854
11855       if (!IS_PLAYER(x,y) &&
11856           (element == EL_EMPTY ||
11857            CAN_GROW_INTO(element) ||
11858            element == EL_QUICKSAND_EMPTY ||
11859            element == EL_QUICKSAND_FAST_EMPTY ||
11860            element == EL_ACID_SPLASH_LEFT ||
11861            element == EL_ACID_SPLASH_RIGHT))
11862       {
11863         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11864             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11865             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11866             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11867           Feld[x][y] = EL_AMOEBA_DROP;
11868       }
11869
11870       random = random * 129 + 1;
11871     }
11872   }
11873 #endif
11874
11875 #if 0
11876   if (game.explosions_delayed)
11877 #endif
11878   {
11879     game.explosions_delayed = FALSE;
11880
11881     SCAN_PLAYFIELD(x, y)
11882     {
11883       element = Feld[x][y];
11884
11885       if (ExplodeField[x][y])
11886         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11887       else if (element == EL_EXPLOSION)
11888         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11889
11890       ExplodeField[x][y] = EX_TYPE_NONE;
11891     }
11892
11893     game.explosions_delayed = TRUE;
11894   }
11895
11896   if (game.magic_wall_active)
11897   {
11898     if (!(game.magic_wall_time_left % 4))
11899     {
11900       int element = Feld[magic_wall_x][magic_wall_y];
11901
11902       if (element == EL_BD_MAGIC_WALL_FULL ||
11903           element == EL_BD_MAGIC_WALL_ACTIVE ||
11904           element == EL_BD_MAGIC_WALL_EMPTYING)
11905         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11906       else if (element == EL_DC_MAGIC_WALL_FULL ||
11907                element == EL_DC_MAGIC_WALL_ACTIVE ||
11908                element == EL_DC_MAGIC_WALL_EMPTYING)
11909         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11910       else
11911         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11912     }
11913
11914     if (game.magic_wall_time_left > 0)
11915     {
11916       game.magic_wall_time_left--;
11917
11918       if (!game.magic_wall_time_left)
11919       {
11920         SCAN_PLAYFIELD(x, y)
11921         {
11922           element = Feld[x][y];
11923
11924           if (element == EL_MAGIC_WALL_ACTIVE ||
11925               element == EL_MAGIC_WALL_FULL)
11926           {
11927             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11928             DrawLevelField(x, y);
11929           }
11930           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11931                    element == EL_BD_MAGIC_WALL_FULL)
11932           {
11933             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11934             DrawLevelField(x, y);
11935           }
11936           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11937                    element == EL_DC_MAGIC_WALL_FULL)
11938           {
11939             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11940             DrawLevelField(x, y);
11941           }
11942         }
11943
11944         game.magic_wall_active = FALSE;
11945       }
11946     }
11947   }
11948
11949   if (game.light_time_left > 0)
11950   {
11951     game.light_time_left--;
11952
11953     if (game.light_time_left == 0)
11954       RedrawAllLightSwitchesAndInvisibleElements();
11955   }
11956
11957   if (game.timegate_time_left > 0)
11958   {
11959     game.timegate_time_left--;
11960
11961     if (game.timegate_time_left == 0)
11962       CloseAllOpenTimegates();
11963   }
11964
11965   if (game.lenses_time_left > 0)
11966   {
11967     game.lenses_time_left--;
11968
11969     if (game.lenses_time_left == 0)
11970       RedrawAllInvisibleElementsForLenses();
11971   }
11972
11973   if (game.magnify_time_left > 0)
11974   {
11975     game.magnify_time_left--;
11976
11977     if (game.magnify_time_left == 0)
11978       RedrawAllInvisibleElementsForMagnifier();
11979   }
11980
11981   for (i = 0; i < MAX_PLAYERS; i++)
11982   {
11983     struct PlayerInfo *player = &stored_player[i];
11984
11985     if (SHIELD_ON(player))
11986     {
11987       if (player->shield_deadly_time_left)
11988         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11989       else if (player->shield_normal_time_left)
11990         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11991     }
11992   }
11993
11994   CheckLevelTime();
11995
11996   DrawAllPlayers();
11997   PlayAllPlayersSound();
11998
11999   if (options.debug)                    /* calculate frames per second */
12000   {
12001     static unsigned long fps_counter = 0;
12002     static int fps_frames = 0;
12003     unsigned long fps_delay_ms = Counter() - fps_counter;
12004
12005     fps_frames++;
12006
12007     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12008     {
12009       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12010
12011       fps_frames = 0;
12012       fps_counter = Counter();
12013     }
12014
12015     redraw_mask |= REDRAW_FPS;
12016   }
12017
12018   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12019
12020   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12021   {
12022     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12023
12024     local_player->show_envelope = 0;
12025   }
12026
12027 #if 0
12028   debug_print_timestamp(0, "stop main loop profiling ");
12029   printf("----------------------------------------------------------\n");
12030 #endif
12031
12032   /* use random number generator in every frame to make it less predictable */
12033   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12034     RND(1);
12035 }
12036
12037 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12038 {
12039   int min_x = x, min_y = y, max_x = x, max_y = y;
12040   int i;
12041
12042   for (i = 0; i < MAX_PLAYERS; i++)
12043   {
12044     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12045
12046     if (!stored_player[i].active || &stored_player[i] == player)
12047       continue;
12048
12049     min_x = MIN(min_x, jx);
12050     min_y = MIN(min_y, jy);
12051     max_x = MAX(max_x, jx);
12052     max_y = MAX(max_y, jy);
12053   }
12054
12055   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12056 }
12057
12058 static boolean AllPlayersInVisibleScreen()
12059 {
12060   int i;
12061
12062   for (i = 0; i < MAX_PLAYERS; i++)
12063   {
12064     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12065
12066     if (!stored_player[i].active)
12067       continue;
12068
12069     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12070       return FALSE;
12071   }
12072
12073   return TRUE;
12074 }
12075
12076 void ScrollLevel(int dx, int dy)
12077 {
12078 #if 1
12079   static Bitmap *bitmap_db_field2 = NULL;
12080   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12081   int x, y;
12082 #else
12083   int i, x, y;
12084 #endif
12085
12086 #if 0
12087   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12088   /* only horizontal XOR vertical scroll direction allowed */
12089   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12090     return;
12091 #endif
12092
12093 #if 1
12094   if (bitmap_db_field2 == NULL)
12095     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12096
12097   /* needed when blitting directly to same bitmap -- should not be needed with
12098      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12099   BlitBitmap(drawto_field, bitmap_db_field2,
12100              FX + TILEX * (dx == -1) - softscroll_offset,
12101              FY + TILEY * (dy == -1) - softscroll_offset,
12102              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12103              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12104              FX + TILEX * (dx == 1) - softscroll_offset,
12105              FY + TILEY * (dy == 1) - softscroll_offset);
12106   BlitBitmap(bitmap_db_field2, drawto_field,
12107              FX + TILEX * (dx == 1) - softscroll_offset,
12108              FY + TILEY * (dy == 1) - softscroll_offset,
12109              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12110              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12111              FX + TILEX * (dx == 1) - softscroll_offset,
12112              FY + TILEY * (dy == 1) - softscroll_offset);
12113
12114 #else
12115
12116 #if 1
12117   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12118   int xsize = (BX2 - BX1 + 1);
12119   int ysize = (BY2 - BY1 + 1);
12120   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12121   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12122   int step  = (start < end ? +1 : -1);
12123
12124   for (i = start; i != end; i += step)
12125   {
12126     BlitBitmap(drawto_field, drawto_field,
12127                FX + TILEX * (dx != 0 ? i + step : 0),
12128                FY + TILEY * (dy != 0 ? i + step : 0),
12129                TILEX * (dx != 0 ? 1 : xsize),
12130                TILEY * (dy != 0 ? 1 : ysize),
12131                FX + TILEX * (dx != 0 ? i : 0),
12132                FY + TILEY * (dy != 0 ? i : 0));
12133   }
12134
12135 #else
12136
12137   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12138
12139   BlitBitmap(drawto_field, drawto_field,
12140              FX + TILEX * (dx == -1) - softscroll_offset,
12141              FY + TILEY * (dy == -1) - softscroll_offset,
12142              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12143              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12144              FX + TILEX * (dx == 1) - softscroll_offset,
12145              FY + TILEY * (dy == 1) - softscroll_offset);
12146 #endif
12147 #endif
12148
12149   if (dx != 0)
12150   {
12151     x = (dx == 1 ? BX1 : BX2);
12152     for (y = BY1; y <= BY2; y++)
12153       DrawScreenField(x, y);
12154   }
12155
12156   if (dy != 0)
12157   {
12158     y = (dy == 1 ? BY1 : BY2);
12159     for (x = BX1; x <= BX2; x++)
12160       DrawScreenField(x, y);
12161   }
12162
12163   redraw_mask |= REDRAW_FIELD;
12164 }
12165
12166 static boolean canFallDown(struct PlayerInfo *player)
12167 {
12168   int jx = player->jx, jy = player->jy;
12169
12170   return (IN_LEV_FIELD(jx, jy + 1) &&
12171           (IS_FREE(jx, jy + 1) ||
12172            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12173           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12174           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12175 }
12176
12177 static boolean canPassField(int x, int y, int move_dir)
12178 {
12179   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12180   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12181   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12182   int nextx = x + dx;
12183   int nexty = y + dy;
12184   int element = Feld[x][y];
12185
12186   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12187           !CAN_MOVE(element) &&
12188           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12189           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12190           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12191 }
12192
12193 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12194 {
12195   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12196   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12197   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12198   int newx = x + dx;
12199   int newy = y + dy;
12200
12201   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12202           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12203           (IS_DIGGABLE(Feld[newx][newy]) ||
12204            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12205            canPassField(newx, newy, move_dir)));
12206 }
12207
12208 static void CheckGravityMovement(struct PlayerInfo *player)
12209 {
12210 #if USE_PLAYER_GRAVITY
12211   if (player->gravity && !player->programmed_action)
12212 #else
12213   if (game.gravity && !player->programmed_action)
12214 #endif
12215   {
12216     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12217     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12218     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12219     int jx = player->jx, jy = player->jy;
12220     boolean player_is_moving_to_valid_field =
12221       (!player_is_snapping &&
12222        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12223         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12224     boolean player_can_fall_down = canFallDown(player);
12225
12226     if (player_can_fall_down &&
12227         !player_is_moving_to_valid_field)
12228       player->programmed_action = MV_DOWN;
12229   }
12230 }
12231
12232 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12233 {
12234   return CheckGravityMovement(player);
12235
12236 #if USE_PLAYER_GRAVITY
12237   if (player->gravity && !player->programmed_action)
12238 #else
12239   if (game.gravity && !player->programmed_action)
12240 #endif
12241   {
12242     int jx = player->jx, jy = player->jy;
12243     boolean field_under_player_is_free =
12244       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12245     boolean player_is_standing_on_valid_field =
12246       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12247        (IS_WALKABLE(Feld[jx][jy]) &&
12248         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12249
12250     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12251       player->programmed_action = MV_DOWN;
12252   }
12253 }
12254
12255 /*
12256   MovePlayerOneStep()
12257   -----------------------------------------------------------------------------
12258   dx, dy:               direction (non-diagonal) to try to move the player to
12259   real_dx, real_dy:     direction as read from input device (can be diagonal)
12260 */
12261
12262 boolean MovePlayerOneStep(struct PlayerInfo *player,
12263                           int dx, int dy, int real_dx, int real_dy)
12264 {
12265   int jx = player->jx, jy = player->jy;
12266   int new_jx = jx + dx, new_jy = jy + dy;
12267 #if !USE_FIXED_DONT_RUN_INTO
12268   int element;
12269 #endif
12270   int can_move;
12271   boolean player_can_move = !player->cannot_move;
12272
12273   if (!player->active || (!dx && !dy))
12274     return MP_NO_ACTION;
12275
12276   player->MovDir = (dx < 0 ? MV_LEFT :
12277                     dx > 0 ? MV_RIGHT :
12278                     dy < 0 ? MV_UP :
12279                     dy > 0 ? MV_DOWN :  MV_NONE);
12280
12281   if (!IN_LEV_FIELD(new_jx, new_jy))
12282     return MP_NO_ACTION;
12283
12284   if (!player_can_move)
12285   {
12286     if (player->MovPos == 0)
12287     {
12288       player->is_moving = FALSE;
12289       player->is_digging = FALSE;
12290       player->is_collecting = FALSE;
12291       player->is_snapping = FALSE;
12292       player->is_pushing = FALSE;
12293     }
12294   }
12295
12296 #if 1
12297   if (!options.network && game.centered_player_nr == -1 &&
12298       !AllPlayersInSight(player, new_jx, new_jy))
12299     return MP_NO_ACTION;
12300 #else
12301   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12302     return MP_NO_ACTION;
12303 #endif
12304
12305 #if !USE_FIXED_DONT_RUN_INTO
12306   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12307
12308   /* (moved to DigField()) */
12309   if (player_can_move && DONT_RUN_INTO(element))
12310   {
12311     if (element == EL_ACID && dx == 0 && dy == 1)
12312     {
12313       SplashAcid(new_jx, new_jy);
12314       Feld[jx][jy] = EL_PLAYER_1;
12315       InitMovingField(jx, jy, MV_DOWN);
12316       Store[jx][jy] = EL_ACID;
12317       ContinueMoving(jx, jy);
12318       BuryPlayer(player);
12319     }
12320     else
12321       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12322
12323     return MP_MOVING;
12324   }
12325 #endif
12326
12327   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12328   if (can_move != MP_MOVING)
12329     return can_move;
12330
12331   /* check if DigField() has caused relocation of the player */
12332   if (player->jx != jx || player->jy != jy)
12333     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12334
12335   StorePlayer[jx][jy] = 0;
12336   player->last_jx = jx;
12337   player->last_jy = jy;
12338   player->jx = new_jx;
12339   player->jy = new_jy;
12340   StorePlayer[new_jx][new_jy] = player->element_nr;
12341
12342   if (player->move_delay_value_next != -1)
12343   {
12344     player->move_delay_value = player->move_delay_value_next;
12345     player->move_delay_value_next = -1;
12346   }
12347
12348   player->MovPos =
12349     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12350
12351   player->step_counter++;
12352
12353   PlayerVisit[jx][jy] = FrameCounter;
12354
12355 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12356   player->is_moving = TRUE;
12357 #endif
12358
12359 #if 1
12360   /* should better be called in MovePlayer(), but this breaks some tapes */
12361   ScrollPlayer(player, SCROLL_INIT);
12362 #endif
12363
12364   return MP_MOVING;
12365 }
12366
12367 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12368 {
12369   int jx = player->jx, jy = player->jy;
12370   int old_jx = jx, old_jy = jy;
12371   int moved = MP_NO_ACTION;
12372
12373   if (!player->active)
12374     return FALSE;
12375
12376   if (!dx && !dy)
12377   {
12378     if (player->MovPos == 0)
12379     {
12380       player->is_moving = FALSE;
12381       player->is_digging = FALSE;
12382       player->is_collecting = FALSE;
12383       player->is_snapping = FALSE;
12384       player->is_pushing = FALSE;
12385     }
12386
12387     return FALSE;
12388   }
12389
12390   if (player->move_delay > 0)
12391     return FALSE;
12392
12393   player->move_delay = -1;              /* set to "uninitialized" value */
12394
12395   /* store if player is automatically moved to next field */
12396   player->is_auto_moving = (player->programmed_action != MV_NONE);
12397
12398   /* remove the last programmed player action */
12399   player->programmed_action = 0;
12400
12401   if (player->MovPos)
12402   {
12403     /* should only happen if pre-1.2 tape recordings are played */
12404     /* this is only for backward compatibility */
12405
12406     int original_move_delay_value = player->move_delay_value;
12407
12408 #if DEBUG
12409     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12410            tape.counter);
12411 #endif
12412
12413     /* scroll remaining steps with finest movement resolution */
12414     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12415
12416     while (player->MovPos)
12417     {
12418       ScrollPlayer(player, SCROLL_GO_ON);
12419       ScrollScreen(NULL, SCROLL_GO_ON);
12420
12421       AdvanceFrameAndPlayerCounters(player->index_nr);
12422
12423       DrawAllPlayers();
12424       BackToFront();
12425     }
12426
12427     player->move_delay_value = original_move_delay_value;
12428   }
12429
12430   player->is_active = FALSE;
12431
12432   if (player->last_move_dir & MV_HORIZONTAL)
12433   {
12434     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12435       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12436   }
12437   else
12438   {
12439     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12440       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12441   }
12442
12443 #if USE_FIXED_BORDER_RUNNING_GFX
12444   if (!moved && !player->is_active)
12445   {
12446     player->is_moving = FALSE;
12447     player->is_digging = FALSE;
12448     player->is_collecting = FALSE;
12449     player->is_snapping = FALSE;
12450     player->is_pushing = FALSE;
12451   }
12452 #endif
12453
12454   jx = player->jx;
12455   jy = player->jy;
12456
12457 #if 1
12458   if (moved & MP_MOVING && !ScreenMovPos &&
12459       (player->index_nr == game.centered_player_nr ||
12460        game.centered_player_nr == -1))
12461 #else
12462   if (moved & MP_MOVING && !ScreenMovPos &&
12463       (player == local_player || !options.network))
12464 #endif
12465   {
12466     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12467     int offset = game.scroll_delay_value;
12468
12469     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12470     {
12471       /* actual player has left the screen -- scroll in that direction */
12472       if (jx != old_jx)         /* player has moved horizontally */
12473         scroll_x += (jx - old_jx);
12474       else                      /* player has moved vertically */
12475         scroll_y += (jy - old_jy);
12476     }
12477     else
12478     {
12479       if (jx != old_jx)         /* player has moved horizontally */
12480       {
12481         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12482             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12483           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12484
12485         /* don't scroll over playfield boundaries */
12486         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12487           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12488
12489         /* don't scroll more than one field at a time */
12490         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12491
12492         /* don't scroll against the player's moving direction */
12493         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12494             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12495           scroll_x = old_scroll_x;
12496       }
12497       else                      /* player has moved vertically */
12498       {
12499         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12500             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12501           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12502
12503         /* don't scroll over playfield boundaries */
12504         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12505           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12506
12507         /* don't scroll more than one field at a time */
12508         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12509
12510         /* don't scroll against the player's moving direction */
12511         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12512             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12513           scroll_y = old_scroll_y;
12514       }
12515     }
12516
12517     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12518     {
12519 #if 1
12520       if (!options.network && game.centered_player_nr == -1 &&
12521           !AllPlayersInVisibleScreen())
12522       {
12523         scroll_x = old_scroll_x;
12524         scroll_y = old_scroll_y;
12525       }
12526       else
12527 #else
12528       if (!options.network && !AllPlayersInVisibleScreen())
12529       {
12530         scroll_x = old_scroll_x;
12531         scroll_y = old_scroll_y;
12532       }
12533       else
12534 #endif
12535       {
12536         ScrollScreen(player, SCROLL_INIT);
12537         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12538       }
12539     }
12540   }
12541
12542   player->StepFrame = 0;
12543
12544   if (moved & MP_MOVING)
12545   {
12546     if (old_jx != jx && old_jy == jy)
12547       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12548     else if (old_jx == jx && old_jy != jy)
12549       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12550
12551     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12552
12553     player->last_move_dir = player->MovDir;
12554     player->is_moving = TRUE;
12555     player->is_snapping = FALSE;
12556     player->is_switching = FALSE;
12557     player->is_dropping = FALSE;
12558     player->is_dropping_pressed = FALSE;
12559     player->drop_pressed_delay = 0;
12560
12561 #if 0
12562     /* should better be called here than above, but this breaks some tapes */
12563     ScrollPlayer(player, SCROLL_INIT);
12564 #endif
12565   }
12566   else
12567   {
12568     CheckGravityMovementWhenNotMoving(player);
12569
12570     player->is_moving = FALSE;
12571
12572     /* at this point, the player is allowed to move, but cannot move right now
12573        (e.g. because of something blocking the way) -- ensure that the player
12574        is also allowed to move in the next frame (in old versions before 3.1.1,
12575        the player was forced to wait again for eight frames before next try) */
12576
12577     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12578       player->move_delay = 0;   /* allow direct movement in the next frame */
12579   }
12580
12581   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12582     player->move_delay = player->move_delay_value;
12583
12584   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12585   {
12586     TestIfPlayerTouchesBadThing(jx, jy);
12587     TestIfPlayerTouchesCustomElement(jx, jy);
12588   }
12589
12590   if (!player->active)
12591     RemovePlayer(player);
12592
12593   return moved;
12594 }
12595
12596 void ScrollPlayer(struct PlayerInfo *player, int mode)
12597 {
12598   int jx = player->jx, jy = player->jy;
12599   int last_jx = player->last_jx, last_jy = player->last_jy;
12600   int move_stepsize = TILEX / player->move_delay_value;
12601
12602 #if USE_NEW_PLAYER_SPEED
12603   if (!player->active)
12604     return;
12605
12606   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12607     return;
12608 #else
12609   if (!player->active || player->MovPos == 0)
12610     return;
12611 #endif
12612
12613   if (mode == SCROLL_INIT)
12614   {
12615     player->actual_frame_counter = FrameCounter;
12616     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12617
12618     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12619         Feld[last_jx][last_jy] == EL_EMPTY)
12620     {
12621       int last_field_block_delay = 0;   /* start with no blocking at all */
12622       int block_delay_adjustment = player->block_delay_adjustment;
12623
12624       /* if player blocks last field, add delay for exactly one move */
12625       if (player->block_last_field)
12626       {
12627         last_field_block_delay += player->move_delay_value;
12628
12629         /* when blocking enabled, prevent moving up despite gravity */
12630 #if USE_PLAYER_GRAVITY
12631         if (player->gravity && player->MovDir == MV_UP)
12632           block_delay_adjustment = -1;
12633 #else
12634         if (game.gravity && player->MovDir == MV_UP)
12635           block_delay_adjustment = -1;
12636 #endif
12637       }
12638
12639       /* add block delay adjustment (also possible when not blocking) */
12640       last_field_block_delay += block_delay_adjustment;
12641
12642       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12643       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12644     }
12645
12646 #if USE_NEW_PLAYER_SPEED
12647     if (player->MovPos != 0)    /* player has not yet reached destination */
12648       return;
12649 #else
12650     return;
12651 #endif
12652   }
12653   else if (!FrameReached(&player->actual_frame_counter, 1))
12654     return;
12655
12656 #if USE_NEW_PLAYER_SPEED
12657   if (player->MovPos != 0)
12658   {
12659     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12660     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12661
12662     /* before DrawPlayer() to draw correct player graphic for this case */
12663     if (player->MovPos == 0)
12664       CheckGravityMovement(player);
12665   }
12666 #else
12667   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12668   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12669
12670   /* before DrawPlayer() to draw correct player graphic for this case */
12671   if (player->MovPos == 0)
12672     CheckGravityMovement(player);
12673 #endif
12674
12675   if (player->MovPos == 0)      /* player reached destination field */
12676   {
12677     if (player->move_delay_reset_counter > 0)
12678     {
12679       player->move_delay_reset_counter--;
12680
12681       if (player->move_delay_reset_counter == 0)
12682       {
12683         /* continue with normal speed after quickly moving through gate */
12684         HALVE_PLAYER_SPEED(player);
12685
12686         /* be able to make the next move without delay */
12687         player->move_delay = 0;
12688       }
12689     }
12690
12691     player->last_jx = jx;
12692     player->last_jy = jy;
12693
12694     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12695         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12696         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12697         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12698         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12699         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12700     {
12701       DrawPlayer(player);       /* needed here only to cleanup last field */
12702       RemovePlayer(player);
12703
12704       if (local_player->friends_still_needed == 0 ||
12705           IS_SP_ELEMENT(Feld[jx][jy]))
12706         PlayerWins(player);
12707     }
12708
12709     /* this breaks one level: "machine", level 000 */
12710     {
12711       int move_direction = player->MovDir;
12712       int enter_side = MV_DIR_OPPOSITE(move_direction);
12713       int leave_side = move_direction;
12714       int old_jx = last_jx;
12715       int old_jy = last_jy;
12716       int old_element = Feld[old_jx][old_jy];
12717       int new_element = Feld[jx][jy];
12718
12719       if (IS_CUSTOM_ELEMENT(old_element))
12720         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12721                                    CE_LEFT_BY_PLAYER,
12722                                    player->index_bit, leave_side);
12723
12724       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12725                                           CE_PLAYER_LEAVES_X,
12726                                           player->index_bit, leave_side);
12727
12728       if (IS_CUSTOM_ELEMENT(new_element))
12729         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12730                                    player->index_bit, enter_side);
12731
12732       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12733                                           CE_PLAYER_ENTERS_X,
12734                                           player->index_bit, enter_side);
12735
12736       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
12737                                         CE_MOVE_OF_X, move_direction);
12738     }
12739
12740     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12741     {
12742       TestIfPlayerTouchesBadThing(jx, jy);
12743       TestIfPlayerTouchesCustomElement(jx, jy);
12744
12745       /* needed because pushed element has not yet reached its destination,
12746          so it would trigger a change event at its previous field location */
12747       if (!player->is_pushing)
12748         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12749
12750       if (!player->active)
12751         RemovePlayer(player);
12752     }
12753
12754     if (!local_player->LevelSolved && level.use_step_counter)
12755     {
12756       int i;
12757
12758       TimePlayed++;
12759
12760       if (TimeLeft > 0)
12761       {
12762         TimeLeft--;
12763
12764         if (TimeLeft <= 10 && setup.time_limit)
12765           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12766
12767 #if 1
12768         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12769
12770         DisplayGameControlValues();
12771 #else
12772         DrawGameValue_Time(TimeLeft);
12773 #endif
12774
12775         if (!TimeLeft && setup.time_limit)
12776           for (i = 0; i < MAX_PLAYERS; i++)
12777             KillPlayer(&stored_player[i]);
12778       }
12779 #if 1
12780       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12781       {
12782         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12783
12784         DisplayGameControlValues();
12785       }
12786 #else
12787       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12788         DrawGameValue_Time(TimePlayed);
12789 #endif
12790     }
12791
12792     if (tape.single_step && tape.recording && !tape.pausing &&
12793         !player->programmed_action)
12794       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12795   }
12796 }
12797
12798 void ScrollScreen(struct PlayerInfo *player, int mode)
12799 {
12800   static unsigned long screen_frame_counter = 0;
12801
12802   if (mode == SCROLL_INIT)
12803   {
12804     /* set scrolling step size according to actual player's moving speed */
12805     ScrollStepSize = TILEX / player->move_delay_value;
12806
12807     screen_frame_counter = FrameCounter;
12808     ScreenMovDir = player->MovDir;
12809     ScreenMovPos = player->MovPos;
12810     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12811     return;
12812   }
12813   else if (!FrameReached(&screen_frame_counter, 1))
12814     return;
12815
12816   if (ScreenMovPos)
12817   {
12818     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12819     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12820     redraw_mask |= REDRAW_FIELD;
12821   }
12822   else
12823     ScreenMovDir = MV_NONE;
12824 }
12825
12826 void TestIfPlayerTouchesCustomElement(int x, int y)
12827 {
12828   static int xy[4][2] =
12829   {
12830     { 0, -1 },
12831     { -1, 0 },
12832     { +1, 0 },
12833     { 0, +1 }
12834   };
12835   static int trigger_sides[4][2] =
12836   {
12837     /* center side       border side */
12838     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12839     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12840     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12841     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12842   };
12843   static int touch_dir[4] =
12844   {
12845     MV_LEFT | MV_RIGHT,
12846     MV_UP   | MV_DOWN,
12847     MV_UP   | MV_DOWN,
12848     MV_LEFT | MV_RIGHT
12849   };
12850   int center_element = Feld[x][y];      /* should always be non-moving! */
12851   int i;
12852
12853   for (i = 0; i < NUM_DIRECTIONS; i++)
12854   {
12855     int xx = x + xy[i][0];
12856     int yy = y + xy[i][1];
12857     int center_side = trigger_sides[i][0];
12858     int border_side = trigger_sides[i][1];
12859     int border_element;
12860
12861     if (!IN_LEV_FIELD(xx, yy))
12862       continue;
12863
12864     if (IS_PLAYER(x, y))
12865     {
12866       struct PlayerInfo *player = PLAYERINFO(x, y);
12867
12868       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12869         border_element = Feld[xx][yy];          /* may be moving! */
12870       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12871         border_element = Feld[xx][yy];
12872       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12873         border_element = MovingOrBlocked2Element(xx, yy);
12874       else
12875         continue;               /* center and border element do not touch */
12876
12877       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12878                                  player->index_bit, border_side);
12879       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12880                                           CE_PLAYER_TOUCHES_X,
12881                                           player->index_bit, border_side);
12882     }
12883     else if (IS_PLAYER(xx, yy))
12884     {
12885       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12886
12887       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12888       {
12889         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12890           continue;             /* center and border element do not touch */
12891       }
12892
12893       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12894                                  player->index_bit, center_side);
12895       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12896                                           CE_PLAYER_TOUCHES_X,
12897                                           player->index_bit, center_side);
12898       break;
12899     }
12900   }
12901 }
12902
12903 #if USE_ELEMENT_TOUCHING_BUGFIX
12904
12905 void TestIfElementTouchesCustomElement(int x, int y)
12906 {
12907   static int xy[4][2] =
12908   {
12909     { 0, -1 },
12910     { -1, 0 },
12911     { +1, 0 },
12912     { 0, +1 }
12913   };
12914   static int trigger_sides[4][2] =
12915   {
12916     /* center side      border side */
12917     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12918     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12919     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12920     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12921   };
12922   static int touch_dir[4] =
12923   {
12924     MV_LEFT | MV_RIGHT,
12925     MV_UP   | MV_DOWN,
12926     MV_UP   | MV_DOWN,
12927     MV_LEFT | MV_RIGHT
12928   };
12929   boolean change_center_element = FALSE;
12930   int center_element = Feld[x][y];      /* should always be non-moving! */
12931   int border_element_old[NUM_DIRECTIONS];
12932   int i;
12933
12934   for (i = 0; i < NUM_DIRECTIONS; i++)
12935   {
12936     int xx = x + xy[i][0];
12937     int yy = y + xy[i][1];
12938     int border_element;
12939
12940     border_element_old[i] = -1;
12941
12942     if (!IN_LEV_FIELD(xx, yy))
12943       continue;
12944
12945     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12946       border_element = Feld[xx][yy];    /* may be moving! */
12947     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12948       border_element = Feld[xx][yy];
12949     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12950       border_element = MovingOrBlocked2Element(xx, yy);
12951     else
12952       continue;                 /* center and border element do not touch */
12953
12954     border_element_old[i] = border_element;
12955   }
12956
12957   for (i = 0; i < NUM_DIRECTIONS; i++)
12958   {
12959     int xx = x + xy[i][0];
12960     int yy = y + xy[i][1];
12961     int center_side = trigger_sides[i][0];
12962     int border_element = border_element_old[i];
12963
12964     if (border_element == -1)
12965       continue;
12966
12967     /* check for change of border element */
12968     CheckElementChangeBySide(xx, yy, border_element, center_element,
12969                              CE_TOUCHING_X, center_side);
12970   }
12971
12972   for (i = 0; i < NUM_DIRECTIONS; i++)
12973   {
12974     int border_side = trigger_sides[i][1];
12975     int border_element = border_element_old[i];
12976
12977     if (border_element == -1)
12978       continue;
12979
12980     /* check for change of center element (but change it only once) */
12981     if (!change_center_element)
12982       change_center_element =
12983         CheckElementChangeBySide(x, y, center_element, border_element,
12984                                  CE_TOUCHING_X, border_side);
12985   }
12986 }
12987
12988 #else
12989
12990 void TestIfElementTouchesCustomElement_OLD(int x, int y)
12991 {
12992   static int xy[4][2] =
12993   {
12994     { 0, -1 },
12995     { -1, 0 },
12996     { +1, 0 },
12997     { 0, +1 }
12998   };
12999   static int trigger_sides[4][2] =
13000   {
13001     /* center side      border side */
13002     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13003     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13004     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13005     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13006   };
13007   static int touch_dir[4] =
13008   {
13009     MV_LEFT | MV_RIGHT,
13010     MV_UP   | MV_DOWN,
13011     MV_UP   | MV_DOWN,
13012     MV_LEFT | MV_RIGHT
13013   };
13014   boolean change_center_element = FALSE;
13015   int center_element = Feld[x][y];      /* should always be non-moving! */
13016   int i;
13017
13018   for (i = 0; i < NUM_DIRECTIONS; i++)
13019   {
13020     int xx = x + xy[i][0];
13021     int yy = y + xy[i][1];
13022     int center_side = trigger_sides[i][0];
13023     int border_side = trigger_sides[i][1];
13024     int border_element;
13025
13026     if (!IN_LEV_FIELD(xx, yy))
13027       continue;
13028
13029     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13030       border_element = Feld[xx][yy];    /* may be moving! */
13031     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13032       border_element = Feld[xx][yy];
13033     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13034       border_element = MovingOrBlocked2Element(xx, yy);
13035     else
13036       continue;                 /* center and border element do not touch */
13037
13038     /* check for change of center element (but change it only once) */
13039     if (!change_center_element)
13040       change_center_element =
13041         CheckElementChangeBySide(x, y, center_element, border_element,
13042                                  CE_TOUCHING_X, border_side);
13043
13044     /* check for change of border element */
13045     CheckElementChangeBySide(xx, yy, border_element, center_element,
13046                              CE_TOUCHING_X, center_side);
13047   }
13048 }
13049
13050 #endif
13051
13052 void TestIfElementHitsCustomElement(int x, int y, int direction)
13053 {
13054   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13055   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13056   int hitx = x + dx, hity = y + dy;
13057   int hitting_element = Feld[x][y];
13058   int touched_element;
13059
13060   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13061     return;
13062
13063   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13064                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13065
13066   if (IN_LEV_FIELD(hitx, hity))
13067   {
13068     int opposite_direction = MV_DIR_OPPOSITE(direction);
13069     int hitting_side = direction;
13070     int touched_side = opposite_direction;
13071     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13072                           MovDir[hitx][hity] != direction ||
13073                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13074
13075     object_hit = TRUE;
13076
13077     if (object_hit)
13078     {
13079       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13080                                CE_HITTING_X, touched_side);
13081
13082       CheckElementChangeBySide(hitx, hity, touched_element,
13083                                hitting_element, CE_HIT_BY_X, hitting_side);
13084
13085       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13086                                CE_HIT_BY_SOMETHING, opposite_direction);
13087     }
13088   }
13089
13090   /* "hitting something" is also true when hitting the playfield border */
13091   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13092                            CE_HITTING_SOMETHING, direction);
13093 }
13094
13095 #if 0
13096 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13097 {
13098   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13099   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13100   int hitx = x + dx, hity = y + dy;
13101   int hitting_element = Feld[x][y];
13102   int touched_element;
13103 #if 0
13104   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13105                         !IS_FREE(hitx, hity) &&
13106                         (!IS_MOVING(hitx, hity) ||
13107                          MovDir[hitx][hity] != direction ||
13108                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13109 #endif
13110
13111   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13112     return;
13113
13114 #if 0
13115   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13116     return;
13117 #endif
13118
13119   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13120                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13121
13122   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13123                            EP_CAN_SMASH_EVERYTHING, direction);
13124
13125   if (IN_LEV_FIELD(hitx, hity))
13126   {
13127     int opposite_direction = MV_DIR_OPPOSITE(direction);
13128     int hitting_side = direction;
13129     int touched_side = opposite_direction;
13130 #if 0
13131     int touched_element = MovingOrBlocked2Element(hitx, hity);
13132 #endif
13133 #if 1
13134     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13135                           MovDir[hitx][hity] != direction ||
13136                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13137
13138     object_hit = TRUE;
13139 #endif
13140
13141     if (object_hit)
13142     {
13143       int i;
13144
13145       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13146                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13147
13148       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13149                                CE_OTHER_IS_SMASHING, touched_side);
13150
13151       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13152                                CE_OTHER_GETS_SMASHED, hitting_side);
13153     }
13154   }
13155 }
13156 #endif
13157
13158 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13159 {
13160   int i, kill_x = -1, kill_y = -1;
13161
13162   int bad_element = -1;
13163   static int test_xy[4][2] =
13164   {
13165     { 0, -1 },
13166     { -1, 0 },
13167     { +1, 0 },
13168     { 0, +1 }
13169   };
13170   static int test_dir[4] =
13171   {
13172     MV_UP,
13173     MV_LEFT,
13174     MV_RIGHT,
13175     MV_DOWN
13176   };
13177
13178   for (i = 0; i < NUM_DIRECTIONS; i++)
13179   {
13180     int test_x, test_y, test_move_dir, test_element;
13181
13182     test_x = good_x + test_xy[i][0];
13183     test_y = good_y + test_xy[i][1];
13184
13185     if (!IN_LEV_FIELD(test_x, test_y))
13186       continue;
13187
13188     test_move_dir =
13189       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13190
13191     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13192
13193     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13194        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13195     */
13196     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13197         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13198     {
13199       kill_x = test_x;
13200       kill_y = test_y;
13201       bad_element = test_element;
13202
13203       break;
13204     }
13205   }
13206
13207   if (kill_x != -1 || kill_y != -1)
13208   {
13209     if (IS_PLAYER(good_x, good_y))
13210     {
13211       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13212
13213       if (player->shield_deadly_time_left > 0 &&
13214           !IS_INDESTRUCTIBLE(bad_element))
13215         Bang(kill_x, kill_y);
13216       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13217         KillPlayer(player);
13218     }
13219     else
13220       Bang(good_x, good_y);
13221   }
13222 }
13223
13224 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13225 {
13226   int i, kill_x = -1, kill_y = -1;
13227   int bad_element = Feld[bad_x][bad_y];
13228   static int test_xy[4][2] =
13229   {
13230     { 0, -1 },
13231     { -1, 0 },
13232     { +1, 0 },
13233     { 0, +1 }
13234   };
13235   static int touch_dir[4] =
13236   {
13237     MV_LEFT | MV_RIGHT,
13238     MV_UP   | MV_DOWN,
13239     MV_UP   | MV_DOWN,
13240     MV_LEFT | MV_RIGHT
13241   };
13242   static int test_dir[4] =
13243   {
13244     MV_UP,
13245     MV_LEFT,
13246     MV_RIGHT,
13247     MV_DOWN
13248   };
13249
13250   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13251     return;
13252
13253   for (i = 0; i < NUM_DIRECTIONS; i++)
13254   {
13255     int test_x, test_y, test_move_dir, test_element;
13256
13257     test_x = bad_x + test_xy[i][0];
13258     test_y = bad_y + test_xy[i][1];
13259     if (!IN_LEV_FIELD(test_x, test_y))
13260       continue;
13261
13262     test_move_dir =
13263       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13264
13265     test_element = Feld[test_x][test_y];
13266
13267     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13268        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13269     */
13270     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13271         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13272     {
13273       /* good thing is player or penguin that does not move away */
13274       if (IS_PLAYER(test_x, test_y))
13275       {
13276         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13277
13278         if (bad_element == EL_ROBOT && player->is_moving)
13279           continue;     /* robot does not kill player if he is moving */
13280
13281         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13282         {
13283           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13284             continue;           /* center and border element do not touch */
13285         }
13286
13287         kill_x = test_x;
13288         kill_y = test_y;
13289         break;
13290       }
13291       else if (test_element == EL_PENGUIN)
13292       {
13293         kill_x = test_x;
13294         kill_y = test_y;
13295         break;
13296       }
13297     }
13298   }
13299
13300   if (kill_x != -1 || kill_y != -1)
13301   {
13302     if (IS_PLAYER(kill_x, kill_y))
13303     {
13304       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13305
13306       if (player->shield_deadly_time_left > 0 &&
13307           !IS_INDESTRUCTIBLE(bad_element))
13308         Bang(bad_x, bad_y);
13309       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13310         KillPlayer(player);
13311     }
13312     else
13313       Bang(kill_x, kill_y);
13314   }
13315 }
13316
13317 void TestIfPlayerTouchesBadThing(int x, int y)
13318 {
13319   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13320 }
13321
13322 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13323 {
13324   TestIfGoodThingHitsBadThing(x, y, move_dir);
13325 }
13326
13327 void TestIfBadThingTouchesPlayer(int x, int y)
13328 {
13329   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13330 }
13331
13332 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13333 {
13334   TestIfBadThingHitsGoodThing(x, y, move_dir);
13335 }
13336
13337 void TestIfFriendTouchesBadThing(int x, int y)
13338 {
13339   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13340 }
13341
13342 void TestIfBadThingTouchesFriend(int x, int y)
13343 {
13344   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13345 }
13346
13347 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13348 {
13349   int i, kill_x = bad_x, kill_y = bad_y;
13350   static int xy[4][2] =
13351   {
13352     { 0, -1 },
13353     { -1, 0 },
13354     { +1, 0 },
13355     { 0, +1 }
13356   };
13357
13358   for (i = 0; i < NUM_DIRECTIONS; i++)
13359   {
13360     int x, y, element;
13361
13362     x = bad_x + xy[i][0];
13363     y = bad_y + xy[i][1];
13364     if (!IN_LEV_FIELD(x, y))
13365       continue;
13366
13367     element = Feld[x][y];
13368     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13369         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13370     {
13371       kill_x = x;
13372       kill_y = y;
13373       break;
13374     }
13375   }
13376
13377   if (kill_x != bad_x || kill_y != bad_y)
13378     Bang(bad_x, bad_y);
13379 }
13380
13381 void KillPlayer(struct PlayerInfo *player)
13382 {
13383   int jx = player->jx, jy = player->jy;
13384
13385   if (!player->active)
13386     return;
13387
13388   /* the following code was introduced to prevent an infinite loop when calling
13389      -> Bang()
13390      -> CheckTriggeredElementChangeExt()
13391      -> ExecuteCustomElementAction()
13392      -> KillPlayer()
13393      -> (infinitely repeating the above sequence of function calls)
13394      which occurs when killing the player while having a CE with the setting
13395      "kill player X when explosion of <player X>"; the solution using a new
13396      field "player->killed" was chosen for backwards compatibility, although
13397      clever use of the fields "player->active" etc. would probably also work */
13398 #if 1
13399   if (player->killed)
13400     return;
13401 #endif
13402
13403   player->killed = TRUE;
13404
13405   /* remove accessible field at the player's position */
13406   Feld[jx][jy] = EL_EMPTY;
13407
13408   /* deactivate shield (else Bang()/Explode() would not work right) */
13409   player->shield_normal_time_left = 0;
13410   player->shield_deadly_time_left = 0;
13411
13412   Bang(jx, jy);
13413   BuryPlayer(player);
13414 }
13415
13416 static void KillPlayerUnlessEnemyProtected(int x, int y)
13417 {
13418   if (!PLAYER_ENEMY_PROTECTED(x, y))
13419     KillPlayer(PLAYERINFO(x, y));
13420 }
13421
13422 static void KillPlayerUnlessExplosionProtected(int x, int y)
13423 {
13424   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13425     KillPlayer(PLAYERINFO(x, y));
13426 }
13427
13428 void BuryPlayer(struct PlayerInfo *player)
13429 {
13430   int jx = player->jx, jy = player->jy;
13431
13432   if (!player->active)
13433     return;
13434
13435   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13436   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13437
13438   player->GameOver = TRUE;
13439   RemovePlayer(player);
13440 }
13441
13442 void RemovePlayer(struct PlayerInfo *player)
13443 {
13444   int jx = player->jx, jy = player->jy;
13445   int i, found = FALSE;
13446
13447   player->present = FALSE;
13448   player->active = FALSE;
13449
13450   if (!ExplodeField[jx][jy])
13451     StorePlayer[jx][jy] = 0;
13452
13453   if (player->is_moving)
13454     DrawLevelField(player->last_jx, player->last_jy);
13455
13456   for (i = 0; i < MAX_PLAYERS; i++)
13457     if (stored_player[i].active)
13458       found = TRUE;
13459
13460   if (!found)
13461     AllPlayersGone = TRUE;
13462
13463   ExitX = ZX = jx;
13464   ExitY = ZY = jy;
13465 }
13466
13467 #if USE_NEW_SNAP_DELAY
13468 static void setFieldForSnapping(int x, int y, int element, int direction)
13469 {
13470   struct ElementInfo *ei = &element_info[element];
13471   int direction_bit = MV_DIR_TO_BIT(direction);
13472   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13473   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13474                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13475
13476   Feld[x][y] = EL_ELEMENT_SNAPPING;
13477   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13478
13479   ResetGfxAnimation(x, y);
13480
13481   GfxElement[x][y] = element;
13482   GfxAction[x][y] = action;
13483   GfxDir[x][y] = direction;
13484   GfxFrame[x][y] = -1;
13485 }
13486 #endif
13487
13488 /*
13489   =============================================================================
13490   checkDiagonalPushing()
13491   -----------------------------------------------------------------------------
13492   check if diagonal input device direction results in pushing of object
13493   (by checking if the alternative direction is walkable, diggable, ...)
13494   =============================================================================
13495 */
13496
13497 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13498                                     int x, int y, int real_dx, int real_dy)
13499 {
13500   int jx, jy, dx, dy, xx, yy;
13501
13502   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13503     return TRUE;
13504
13505   /* diagonal direction: check alternative direction */
13506   jx = player->jx;
13507   jy = player->jy;
13508   dx = x - jx;
13509   dy = y - jy;
13510   xx = jx + (dx == 0 ? real_dx : 0);
13511   yy = jy + (dy == 0 ? real_dy : 0);
13512
13513   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13514 }
13515
13516 /*
13517   =============================================================================
13518   DigField()
13519   -----------------------------------------------------------------------------
13520   x, y:                 field next to player (non-diagonal) to try to dig to
13521   real_dx, real_dy:     direction as read from input device (can be diagonal)
13522   =============================================================================
13523 */
13524
13525 int DigField(struct PlayerInfo *player,
13526              int oldx, int oldy, int x, int y,
13527              int real_dx, int real_dy, int mode)
13528 {
13529   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13530   boolean player_was_pushing = player->is_pushing;
13531   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13532   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13533   int jx = oldx, jy = oldy;
13534   int dx = x - jx, dy = y - jy;
13535   int nextx = x + dx, nexty = y + dy;
13536   int move_direction = (dx == -1 ? MV_LEFT  :
13537                         dx == +1 ? MV_RIGHT :
13538                         dy == -1 ? MV_UP    :
13539                         dy == +1 ? MV_DOWN  : MV_NONE);
13540   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13541   int dig_side = MV_DIR_OPPOSITE(move_direction);
13542   int old_element = Feld[jx][jy];
13543 #if USE_FIXED_DONT_RUN_INTO
13544   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13545 #else
13546   int element;
13547 #endif
13548   int collect_count;
13549
13550   if (is_player)                /* function can also be called by EL_PENGUIN */
13551   {
13552     if (player->MovPos == 0)
13553     {
13554       player->is_digging = FALSE;
13555       player->is_collecting = FALSE;
13556     }
13557
13558     if (player->MovPos == 0)    /* last pushing move finished */
13559       player->is_pushing = FALSE;
13560
13561     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13562     {
13563       player->is_switching = FALSE;
13564       player->push_delay = -1;
13565
13566       return MP_NO_ACTION;
13567     }
13568   }
13569
13570 #if !USE_FIXED_DONT_RUN_INTO
13571   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13572     return MP_NO_ACTION;
13573 #endif
13574
13575   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13576     old_element = Back[jx][jy];
13577
13578   /* in case of element dropped at player position, check background */
13579   else if (Back[jx][jy] != EL_EMPTY &&
13580            game.engine_version >= VERSION_IDENT(2,2,0,0))
13581     old_element = Back[jx][jy];
13582
13583   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13584     return MP_NO_ACTION;        /* field has no opening in this direction */
13585
13586   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13587     return MP_NO_ACTION;        /* field has no opening in this direction */
13588
13589 #if USE_FIXED_DONT_RUN_INTO
13590   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13591   {
13592     SplashAcid(x, y);
13593
13594     Feld[jx][jy] = player->artwork_element;
13595     InitMovingField(jx, jy, MV_DOWN);
13596     Store[jx][jy] = EL_ACID;
13597     ContinueMoving(jx, jy);
13598     BuryPlayer(player);
13599
13600     return MP_DONT_RUN_INTO;
13601   }
13602 #endif
13603
13604 #if USE_FIXED_DONT_RUN_INTO
13605   if (player_can_move && DONT_RUN_INTO(element))
13606   {
13607     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13608
13609     return MP_DONT_RUN_INTO;
13610   }
13611 #endif
13612
13613 #if USE_FIXED_DONT_RUN_INTO
13614   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13615     return MP_NO_ACTION;
13616 #endif
13617
13618 #if !USE_FIXED_DONT_RUN_INTO
13619   element = Feld[x][y];
13620 #endif
13621
13622   collect_count = element_info[element].collect_count_initial;
13623
13624   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13625     return MP_NO_ACTION;
13626
13627   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13628     player_can_move = player_can_move_or_snap;
13629
13630   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13631       game.engine_version >= VERSION_IDENT(2,2,0,0))
13632   {
13633     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13634                                player->index_bit, dig_side);
13635     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13636                                         player->index_bit, dig_side);
13637
13638     if (element == EL_DC_LANDMINE)
13639       Bang(x, y);
13640
13641     if (Feld[x][y] != element)          /* field changed by snapping */
13642       return MP_ACTION;
13643
13644     return MP_NO_ACTION;
13645   }
13646
13647 #if USE_PLAYER_GRAVITY
13648   if (player->gravity && is_player && !player->is_auto_moving &&
13649       canFallDown(player) && move_direction != MV_DOWN &&
13650       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13651     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13652 #else
13653   if (game.gravity && is_player && !player->is_auto_moving &&
13654       canFallDown(player) && move_direction != MV_DOWN &&
13655       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13656     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13657 #endif
13658
13659   if (player_can_move &&
13660       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13661   {
13662     int sound_element = SND_ELEMENT(element);
13663     int sound_action = ACTION_WALKING;
13664
13665     if (IS_RND_GATE(element))
13666     {
13667       if (!player->key[RND_GATE_NR(element)])
13668         return MP_NO_ACTION;
13669     }
13670     else if (IS_RND_GATE_GRAY(element))
13671     {
13672       if (!player->key[RND_GATE_GRAY_NR(element)])
13673         return MP_NO_ACTION;
13674     }
13675     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13676     {
13677       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13678         return MP_NO_ACTION;
13679     }
13680     else if (element == EL_EXIT_OPEN ||
13681              element == EL_EM_EXIT_OPEN ||
13682              element == EL_STEEL_EXIT_OPEN ||
13683              element == EL_EM_STEEL_EXIT_OPEN ||
13684              element == EL_SP_EXIT_OPEN ||
13685              element == EL_SP_EXIT_OPENING)
13686     {
13687       sound_action = ACTION_PASSING;    /* player is passing exit */
13688     }
13689     else if (element == EL_EMPTY)
13690     {
13691       sound_action = ACTION_MOVING;             /* nothing to walk on */
13692     }
13693
13694     /* play sound from background or player, whatever is available */
13695     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13696       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13697     else
13698       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13699   }
13700   else if (player_can_move &&
13701            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13702   {
13703     if (!ACCESS_FROM(element, opposite_direction))
13704       return MP_NO_ACTION;      /* field not accessible from this direction */
13705
13706     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13707       return MP_NO_ACTION;
13708
13709     if (IS_EM_GATE(element))
13710     {
13711       if (!player->key[EM_GATE_NR(element)])
13712         return MP_NO_ACTION;
13713     }
13714     else if (IS_EM_GATE_GRAY(element))
13715     {
13716       if (!player->key[EM_GATE_GRAY_NR(element)])
13717         return MP_NO_ACTION;
13718     }
13719     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13720     {
13721       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13722         return MP_NO_ACTION;
13723     }
13724     else if (IS_EMC_GATE(element))
13725     {
13726       if (!player->key[EMC_GATE_NR(element)])
13727         return MP_NO_ACTION;
13728     }
13729     else if (IS_EMC_GATE_GRAY(element))
13730     {
13731       if (!player->key[EMC_GATE_GRAY_NR(element)])
13732         return MP_NO_ACTION;
13733     }
13734     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13735     {
13736       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13737         return MP_NO_ACTION;
13738     }
13739     else if (element == EL_DC_GATE_WHITE ||
13740              element == EL_DC_GATE_WHITE_GRAY ||
13741              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13742     {
13743       if (player->num_white_keys == 0)
13744         return MP_NO_ACTION;
13745
13746       player->num_white_keys--;
13747     }
13748     else if (IS_SP_PORT(element))
13749     {
13750       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13751           element == EL_SP_GRAVITY_PORT_RIGHT ||
13752           element == EL_SP_GRAVITY_PORT_UP ||
13753           element == EL_SP_GRAVITY_PORT_DOWN)
13754 #if USE_PLAYER_GRAVITY
13755         player->gravity = !player->gravity;
13756 #else
13757         game.gravity = !game.gravity;
13758 #endif
13759       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13760                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13761                element == EL_SP_GRAVITY_ON_PORT_UP ||
13762                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13763 #if USE_PLAYER_GRAVITY
13764         player->gravity = TRUE;
13765 #else
13766         game.gravity = TRUE;
13767 #endif
13768       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13769                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13770                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13771                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13772 #if USE_PLAYER_GRAVITY
13773         player->gravity = FALSE;
13774 #else
13775         game.gravity = FALSE;
13776 #endif
13777     }
13778
13779     /* automatically move to the next field with double speed */
13780     player->programmed_action = move_direction;
13781
13782     if (player->move_delay_reset_counter == 0)
13783     {
13784       player->move_delay_reset_counter = 2;     /* two double speed steps */
13785
13786       DOUBLE_PLAYER_SPEED(player);
13787     }
13788
13789     PlayLevelSoundAction(x, y, ACTION_PASSING);
13790   }
13791   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13792   {
13793     RemoveField(x, y);
13794
13795     if (mode != DF_SNAP)
13796     {
13797       GfxElement[x][y] = GFX_ELEMENT(element);
13798       player->is_digging = TRUE;
13799     }
13800
13801     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13802
13803     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13804                                         player->index_bit, dig_side);
13805
13806     if (mode == DF_SNAP)
13807     {
13808 #if USE_NEW_SNAP_DELAY
13809       if (level.block_snap_field)
13810         setFieldForSnapping(x, y, element, move_direction);
13811       else
13812         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13813 #else
13814       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13815 #endif
13816
13817       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13818                                           player->index_bit, dig_side);
13819     }
13820   }
13821   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13822   {
13823     RemoveField(x, y);
13824
13825     if (is_player && mode != DF_SNAP)
13826     {
13827       GfxElement[x][y] = element;
13828       player->is_collecting = TRUE;
13829     }
13830
13831     if (element == EL_SPEED_PILL)
13832     {
13833       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13834     }
13835     else if (element == EL_EXTRA_TIME && level.time > 0)
13836     {
13837       TimeLeft += level.extra_time;
13838
13839 #if 1
13840       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13841
13842       DisplayGameControlValues();
13843 #else
13844       DrawGameValue_Time(TimeLeft);
13845 #endif
13846     }
13847     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13848     {
13849       player->shield_normal_time_left += level.shield_normal_time;
13850       if (element == EL_SHIELD_DEADLY)
13851         player->shield_deadly_time_left += level.shield_deadly_time;
13852     }
13853     else if (element == EL_DYNAMITE ||
13854              element == EL_EM_DYNAMITE ||
13855              element == EL_SP_DISK_RED)
13856     {
13857       if (player->inventory_size < MAX_INVENTORY_SIZE)
13858         player->inventory_element[player->inventory_size++] = element;
13859
13860       DrawGameDoorValues();
13861     }
13862     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13863     {
13864       player->dynabomb_count++;
13865       player->dynabombs_left++;
13866     }
13867     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13868     {
13869       player->dynabomb_size++;
13870     }
13871     else if (element == EL_DYNABOMB_INCREASE_POWER)
13872     {
13873       player->dynabomb_xl = TRUE;
13874     }
13875     else if (IS_KEY(element))
13876     {
13877       player->key[KEY_NR(element)] = TRUE;
13878
13879       DrawGameDoorValues();
13880     }
13881     else if (element == EL_DC_KEY_WHITE)
13882     {
13883       player->num_white_keys++;
13884
13885       /* display white keys? */
13886       /* DrawGameDoorValues(); */
13887     }
13888     else if (IS_ENVELOPE(element))
13889     {
13890       player->show_envelope = element;
13891     }
13892     else if (element == EL_EMC_LENSES)
13893     {
13894       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13895
13896       RedrawAllInvisibleElementsForLenses();
13897     }
13898     else if (element == EL_EMC_MAGNIFIER)
13899     {
13900       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13901
13902       RedrawAllInvisibleElementsForMagnifier();
13903     }
13904     else if (IS_DROPPABLE(element) ||
13905              IS_THROWABLE(element))     /* can be collected and dropped */
13906     {
13907       int i;
13908
13909       if (collect_count == 0)
13910         player->inventory_infinite_element = element;
13911       else
13912         for (i = 0; i < collect_count; i++)
13913           if (player->inventory_size < MAX_INVENTORY_SIZE)
13914             player->inventory_element[player->inventory_size++] = element;
13915
13916       DrawGameDoorValues();
13917     }
13918     else if (collect_count > 0)
13919     {
13920       local_player->gems_still_needed -= collect_count;
13921       if (local_player->gems_still_needed < 0)
13922         local_player->gems_still_needed = 0;
13923
13924 #if 1
13925       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13926
13927       DisplayGameControlValues();
13928 #else
13929       DrawGameValue_Emeralds(local_player->gems_still_needed);
13930 #endif
13931     }
13932
13933     RaiseScoreElement(element);
13934     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13935
13936     if (is_player)
13937       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13938                                           player->index_bit, dig_side);
13939
13940     if (mode == DF_SNAP)
13941     {
13942 #if USE_NEW_SNAP_DELAY
13943       if (level.block_snap_field)
13944         setFieldForSnapping(x, y, element, move_direction);
13945       else
13946         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13947 #else
13948       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
13949 #endif
13950
13951       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13952                                           player->index_bit, dig_side);
13953     }
13954   }
13955   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13956   {
13957     if (mode == DF_SNAP && element != EL_BD_ROCK)
13958       return MP_NO_ACTION;
13959
13960     if (CAN_FALL(element) && dy)
13961       return MP_NO_ACTION;
13962
13963     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13964         !(element == EL_SPRING && level.use_spring_bug))
13965       return MP_NO_ACTION;
13966
13967     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13968         ((move_direction & MV_VERTICAL &&
13969           ((element_info[element].move_pattern & MV_LEFT &&
13970             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13971            (element_info[element].move_pattern & MV_RIGHT &&
13972             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13973          (move_direction & MV_HORIZONTAL &&
13974           ((element_info[element].move_pattern & MV_UP &&
13975             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13976            (element_info[element].move_pattern & MV_DOWN &&
13977             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13978       return MP_NO_ACTION;
13979
13980     /* do not push elements already moving away faster than player */
13981     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13982         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13983       return MP_NO_ACTION;
13984
13985     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13986     {
13987       if (player->push_delay_value == -1 || !player_was_pushing)
13988         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13989     }
13990     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13991     {
13992       if (player->push_delay_value == -1)
13993         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13994     }
13995     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13996     {
13997       if (!player->is_pushing)
13998         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13999     }
14000
14001     player->is_pushing = TRUE;
14002     player->is_active = TRUE;
14003
14004     if (!(IN_LEV_FIELD(nextx, nexty) &&
14005           (IS_FREE(nextx, nexty) ||
14006            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14007             IS_SB_ELEMENT(element)))))
14008       return MP_NO_ACTION;
14009
14010     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14011       return MP_NO_ACTION;
14012
14013     if (player->push_delay == -1)       /* new pushing; restart delay */
14014       player->push_delay = 0;
14015
14016     if (player->push_delay < player->push_delay_value &&
14017         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14018         element != EL_SPRING && element != EL_BALLOON)
14019     {
14020       /* make sure that there is no move delay before next try to push */
14021       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14022         player->move_delay = 0;
14023
14024       return MP_NO_ACTION;
14025     }
14026
14027     if (IS_SB_ELEMENT(element))
14028     {
14029       if (element == EL_SOKOBAN_FIELD_FULL)
14030       {
14031         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14032         local_player->sokobanfields_still_needed++;
14033       }
14034
14035       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14036       {
14037         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14038         local_player->sokobanfields_still_needed--;
14039       }
14040
14041       Feld[x][y] = EL_SOKOBAN_OBJECT;
14042
14043       if (Back[x][y] == Back[nextx][nexty])
14044         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14045       else if (Back[x][y] != 0)
14046         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14047                                     ACTION_EMPTYING);
14048       else
14049         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14050                                     ACTION_FILLING);
14051
14052       if (local_player->sokobanfields_still_needed == 0 &&
14053           game.emulation == EMU_SOKOBAN)
14054       {
14055         PlayerWins(player);
14056
14057         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14058       }
14059     }
14060     else
14061       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14062
14063     InitMovingField(x, y, move_direction);
14064     GfxAction[x][y] = ACTION_PUSHING;
14065
14066     if (mode == DF_SNAP)
14067       ContinueMoving(x, y);
14068     else
14069       MovPos[x][y] = (dx != 0 ? dx : dy);
14070
14071     Pushed[x][y] = TRUE;
14072     Pushed[nextx][nexty] = TRUE;
14073
14074     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14075       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14076     else
14077       player->push_delay_value = -1;    /* get new value later */
14078
14079     /* check for element change _after_ element has been pushed */
14080     if (game.use_change_when_pushing_bug)
14081     {
14082       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14083                                  player->index_bit, dig_side);
14084       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14085                                           player->index_bit, dig_side);
14086     }
14087   }
14088   else if (IS_SWITCHABLE(element))
14089   {
14090     if (PLAYER_SWITCHING(player, x, y))
14091     {
14092       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14093                                           player->index_bit, dig_side);
14094
14095       return MP_ACTION;
14096     }
14097
14098     player->is_switching = TRUE;
14099     player->switch_x = x;
14100     player->switch_y = y;
14101
14102     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14103
14104     if (element == EL_ROBOT_WHEEL)
14105     {
14106       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14107       ZX = x;
14108       ZY = y;
14109
14110       game.robot_wheel_active = TRUE;
14111
14112       DrawLevelField(x, y);
14113     }
14114     else if (element == EL_SP_TERMINAL)
14115     {
14116       int xx, yy;
14117
14118       SCAN_PLAYFIELD(xx, yy)
14119       {
14120         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14121           Bang(xx, yy);
14122         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14123           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14124       }
14125     }
14126     else if (IS_BELT_SWITCH(element))
14127     {
14128       ToggleBeltSwitch(x, y);
14129     }
14130     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14131              element == EL_SWITCHGATE_SWITCH_DOWN ||
14132              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14133              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14134     {
14135       ToggleSwitchgateSwitch(x, y);
14136     }
14137     else if (element == EL_LIGHT_SWITCH ||
14138              element == EL_LIGHT_SWITCH_ACTIVE)
14139     {
14140       ToggleLightSwitch(x, y);
14141     }
14142     else if (element == EL_TIMEGATE_SWITCH ||
14143              element == EL_DC_TIMEGATE_SWITCH)
14144     {
14145       ActivateTimegateSwitch(x, y);
14146     }
14147     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14148              element == EL_BALLOON_SWITCH_RIGHT ||
14149              element == EL_BALLOON_SWITCH_UP    ||
14150              element == EL_BALLOON_SWITCH_DOWN  ||
14151              element == EL_BALLOON_SWITCH_NONE  ||
14152              element == EL_BALLOON_SWITCH_ANY)
14153     {
14154       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14155                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14156                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14157                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14158                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14159                              move_direction);
14160     }
14161     else if (element == EL_LAMP)
14162     {
14163       Feld[x][y] = EL_LAMP_ACTIVE;
14164       local_player->lights_still_needed--;
14165
14166       ResetGfxAnimation(x, y);
14167       DrawLevelField(x, y);
14168     }
14169     else if (element == EL_TIME_ORB_FULL)
14170     {
14171       Feld[x][y] = EL_TIME_ORB_EMPTY;
14172
14173       if (level.time > 0 || level.use_time_orb_bug)
14174       {
14175         TimeLeft += level.time_orb_time;
14176
14177 #if 1
14178         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14179
14180         DisplayGameControlValues();
14181 #else
14182         DrawGameValue_Time(TimeLeft);
14183 #endif
14184       }
14185
14186       ResetGfxAnimation(x, y);
14187       DrawLevelField(x, y);
14188     }
14189     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14190              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14191     {
14192       int xx, yy;
14193
14194       game.ball_state = !game.ball_state;
14195
14196       SCAN_PLAYFIELD(xx, yy)
14197       {
14198         int e = Feld[xx][yy];
14199
14200         if (game.ball_state)
14201         {
14202           if (e == EL_EMC_MAGIC_BALL)
14203             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14204           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14205             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14206         }
14207         else
14208         {
14209           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14210             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14211           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14212             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14213         }
14214       }
14215     }
14216
14217     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14218                                         player->index_bit, dig_side);
14219
14220     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14221                                         player->index_bit, dig_side);
14222
14223     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14224                                         player->index_bit, dig_side);
14225
14226     return MP_ACTION;
14227   }
14228   else
14229   {
14230     if (!PLAYER_SWITCHING(player, x, y))
14231     {
14232       player->is_switching = TRUE;
14233       player->switch_x = x;
14234       player->switch_y = y;
14235
14236       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14237                                  player->index_bit, dig_side);
14238       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14239                                           player->index_bit, dig_side);
14240
14241       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14242                                  player->index_bit, dig_side);
14243       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14244                                           player->index_bit, dig_side);
14245     }
14246
14247     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14248                                player->index_bit, dig_side);
14249     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14250                                         player->index_bit, dig_side);
14251
14252     return MP_NO_ACTION;
14253   }
14254
14255   player->push_delay = -1;
14256
14257   if (is_player)                /* function can also be called by EL_PENGUIN */
14258   {
14259     if (Feld[x][y] != element)          /* really digged/collected something */
14260     {
14261       player->is_collecting = !player->is_digging;
14262       player->is_active = TRUE;
14263     }
14264   }
14265
14266   return MP_MOVING;
14267 }
14268
14269 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14270 {
14271   int jx = player->jx, jy = player->jy;
14272   int x = jx + dx, y = jy + dy;
14273   int snap_direction = (dx == -1 ? MV_LEFT  :
14274                         dx == +1 ? MV_RIGHT :
14275                         dy == -1 ? MV_UP    :
14276                         dy == +1 ? MV_DOWN  : MV_NONE);
14277   boolean can_continue_snapping = (level.continuous_snapping &&
14278                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14279
14280   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14281     return FALSE;
14282
14283   if (!player->active || !IN_LEV_FIELD(x, y))
14284     return FALSE;
14285
14286   if (dx && dy)
14287     return FALSE;
14288
14289   if (!dx && !dy)
14290   {
14291     if (player->MovPos == 0)
14292       player->is_pushing = FALSE;
14293
14294     player->is_snapping = FALSE;
14295
14296     if (player->MovPos == 0)
14297     {
14298       player->is_moving = FALSE;
14299       player->is_digging = FALSE;
14300       player->is_collecting = FALSE;
14301     }
14302
14303     return FALSE;
14304   }
14305
14306 #if USE_NEW_CONTINUOUS_SNAPPING
14307   /* prevent snapping with already pressed snap key when not allowed */
14308   if (player->is_snapping && !can_continue_snapping)
14309     return FALSE;
14310 #else
14311   if (player->is_snapping)
14312     return FALSE;
14313 #endif
14314
14315   player->MovDir = snap_direction;
14316
14317   if (player->MovPos == 0)
14318   {
14319     player->is_moving = FALSE;
14320     player->is_digging = FALSE;
14321     player->is_collecting = FALSE;
14322   }
14323
14324   player->is_dropping = FALSE;
14325   player->is_dropping_pressed = FALSE;
14326   player->drop_pressed_delay = 0;
14327
14328   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14329     return FALSE;
14330
14331   player->is_snapping = TRUE;
14332   player->is_active = TRUE;
14333
14334   if (player->MovPos == 0)
14335   {
14336     player->is_moving = FALSE;
14337     player->is_digging = FALSE;
14338     player->is_collecting = FALSE;
14339   }
14340
14341   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14342     DrawLevelField(player->last_jx, player->last_jy);
14343
14344   DrawLevelField(x, y);
14345
14346   return TRUE;
14347 }
14348
14349 boolean DropElement(struct PlayerInfo *player)
14350 {
14351   int old_element, new_element;
14352   int dropx = player->jx, dropy = player->jy;
14353   int drop_direction = player->MovDir;
14354   int drop_side = drop_direction;
14355 #if 1
14356   int drop_element = get_next_dropped_element(player);
14357 #else
14358   int drop_element = (player->inventory_size > 0 ?
14359                       player->inventory_element[player->inventory_size - 1] :
14360                       player->inventory_infinite_element != EL_UNDEFINED ?
14361                       player->inventory_infinite_element :
14362                       player->dynabombs_left > 0 ?
14363                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14364                       EL_UNDEFINED);
14365 #endif
14366
14367   player->is_dropping_pressed = TRUE;
14368
14369   /* do not drop an element on top of another element; when holding drop key
14370      pressed without moving, dropped element must move away before the next
14371      element can be dropped (this is especially important if the next element
14372      is dynamite, which can be placed on background for historical reasons) */
14373   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14374     return MP_ACTION;
14375
14376   if (IS_THROWABLE(drop_element))
14377   {
14378     dropx += GET_DX_FROM_DIR(drop_direction);
14379     dropy += GET_DY_FROM_DIR(drop_direction);
14380
14381     if (!IN_LEV_FIELD(dropx, dropy))
14382       return FALSE;
14383   }
14384
14385   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14386   new_element = drop_element;           /* default: no change when dropping */
14387
14388   /* check if player is active, not moving and ready to drop */
14389   if (!player->active || player->MovPos || player->drop_delay > 0)
14390     return FALSE;
14391
14392   /* check if player has anything that can be dropped */
14393   if (new_element == EL_UNDEFINED)
14394     return FALSE;
14395
14396   /* check if drop key was pressed long enough for EM style dynamite */
14397   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14398     return FALSE;
14399
14400   /* check if anything can be dropped at the current position */
14401   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14402     return FALSE;
14403
14404   /* collected custom elements can only be dropped on empty fields */
14405   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14406     return FALSE;
14407
14408   if (old_element != EL_EMPTY)
14409     Back[dropx][dropy] = old_element;   /* store old element on this field */
14410
14411   ResetGfxAnimation(dropx, dropy);
14412   ResetRandomAnimationValue(dropx, dropy);
14413
14414   if (player->inventory_size > 0 ||
14415       player->inventory_infinite_element != EL_UNDEFINED)
14416   {
14417     if (player->inventory_size > 0)
14418     {
14419       player->inventory_size--;
14420
14421       DrawGameDoorValues();
14422
14423       if (new_element == EL_DYNAMITE)
14424         new_element = EL_DYNAMITE_ACTIVE;
14425       else if (new_element == EL_EM_DYNAMITE)
14426         new_element = EL_EM_DYNAMITE_ACTIVE;
14427       else if (new_element == EL_SP_DISK_RED)
14428         new_element = EL_SP_DISK_RED_ACTIVE;
14429     }
14430
14431     Feld[dropx][dropy] = new_element;
14432
14433     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14434       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14435                           el2img(Feld[dropx][dropy]), 0);
14436
14437     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14438
14439     /* needed if previous element just changed to "empty" in the last frame */
14440     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14441
14442     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14443                                player->index_bit, drop_side);
14444     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14445                                         CE_PLAYER_DROPS_X,
14446                                         player->index_bit, drop_side);
14447
14448     TestIfElementTouchesCustomElement(dropx, dropy);
14449   }
14450   else          /* player is dropping a dyna bomb */
14451   {
14452     player->dynabombs_left--;
14453
14454     Feld[dropx][dropy] = new_element;
14455
14456     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14457       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14458                           el2img(Feld[dropx][dropy]), 0);
14459
14460     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14461   }
14462
14463   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14464     InitField_WithBug1(dropx, dropy, FALSE);
14465
14466   new_element = Feld[dropx][dropy];     /* element might have changed */
14467
14468   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14469       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14470   {
14471     int move_direction, nextx, nexty;
14472
14473     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14474       MovDir[dropx][dropy] = drop_direction;
14475
14476     move_direction = MovDir[dropx][dropy];
14477     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14478     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14479
14480     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14481
14482 #if USE_FIX_IMPACT_COLLISION
14483     /* do not cause impact style collision by dropping elements that can fall */
14484     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14485 #else
14486     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14487 #endif
14488   }
14489
14490   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14491   player->is_dropping = TRUE;
14492
14493   player->drop_pressed_delay = 0;
14494   player->is_dropping_pressed = FALSE;
14495
14496   player->drop_x = dropx;
14497   player->drop_y = dropy;
14498
14499   return TRUE;
14500 }
14501
14502 /* ------------------------------------------------------------------------- */
14503 /* game sound playing functions                                              */
14504 /* ------------------------------------------------------------------------- */
14505
14506 static int *loop_sound_frame = NULL;
14507 static int *loop_sound_volume = NULL;
14508
14509 void InitPlayLevelSound()
14510 {
14511   int num_sounds = getSoundListSize();
14512
14513   checked_free(loop_sound_frame);
14514   checked_free(loop_sound_volume);
14515
14516   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14517   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14518 }
14519
14520 static void PlayLevelSound(int x, int y, int nr)
14521 {
14522   int sx = SCREENX(x), sy = SCREENY(y);
14523   int volume, stereo_position;
14524   int max_distance = 8;
14525   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14526
14527   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14528       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14529     return;
14530
14531   if (!IN_LEV_FIELD(x, y) ||
14532       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14533       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14534     return;
14535
14536   volume = SOUND_MAX_VOLUME;
14537
14538   if (!IN_SCR_FIELD(sx, sy))
14539   {
14540     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14541     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14542
14543     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14544   }
14545
14546   stereo_position = (SOUND_MAX_LEFT +
14547                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14548                      (SCR_FIELDX + 2 * max_distance));
14549
14550   if (IS_LOOP_SOUND(nr))
14551   {
14552     /* This assures that quieter loop sounds do not overwrite louder ones,
14553        while restarting sound volume comparison with each new game frame. */
14554
14555     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14556       return;
14557
14558     loop_sound_volume[nr] = volume;
14559     loop_sound_frame[nr] = FrameCounter;
14560   }
14561
14562   PlaySoundExt(nr, volume, stereo_position, type);
14563 }
14564
14565 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14566 {
14567   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14568                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14569                  y < LEVELY(BY1) ? LEVELY(BY1) :
14570                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14571                  sound_action);
14572 }
14573
14574 static void PlayLevelSoundAction(int x, int y, int action)
14575 {
14576   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14577 }
14578
14579 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14580 {
14581   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14582
14583   if (sound_effect != SND_UNDEFINED)
14584     PlayLevelSound(x, y, sound_effect);
14585 }
14586
14587 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14588                                               int action)
14589 {
14590   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14591
14592   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14593     PlayLevelSound(x, y, sound_effect);
14594 }
14595
14596 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14597 {
14598   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14599
14600   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14601     PlayLevelSound(x, y, sound_effect);
14602 }
14603
14604 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14605 {
14606   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14607
14608   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14609     StopSound(sound_effect);
14610 }
14611
14612 static void PlayLevelMusic()
14613 {
14614   if (levelset.music[level_nr] != MUS_UNDEFINED)
14615     PlayMusic(levelset.music[level_nr]);        /* from config file */
14616   else
14617     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14618 }
14619
14620 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14621 {
14622   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14623   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14624   int x = xx - 1 - offset;
14625   int y = yy - 1 - offset;
14626
14627   switch (sample)
14628   {
14629     case SAMPLE_blank:
14630       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14631       break;
14632
14633     case SAMPLE_roll:
14634       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14635       break;
14636
14637     case SAMPLE_stone:
14638       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14639       break;
14640
14641     case SAMPLE_nut:
14642       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14643       break;
14644
14645     case SAMPLE_crack:
14646       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14647       break;
14648
14649     case SAMPLE_bug:
14650       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14651       break;
14652
14653     case SAMPLE_tank:
14654       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14655       break;
14656
14657     case SAMPLE_android_clone:
14658       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14659       break;
14660
14661     case SAMPLE_android_move:
14662       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14663       break;
14664
14665     case SAMPLE_spring:
14666       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14667       break;
14668
14669     case SAMPLE_slurp:
14670       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14671       break;
14672
14673     case SAMPLE_eater:
14674       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14675       break;
14676
14677     case SAMPLE_eater_eat:
14678       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14679       break;
14680
14681     case SAMPLE_alien:
14682       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14683       break;
14684
14685     case SAMPLE_collect:
14686       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14687       break;
14688
14689     case SAMPLE_diamond:
14690       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14691       break;
14692
14693     case SAMPLE_squash:
14694       /* !!! CHECK THIS !!! */
14695 #if 1
14696       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14697 #else
14698       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14699 #endif
14700       break;
14701
14702     case SAMPLE_wonderfall:
14703       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14704       break;
14705
14706     case SAMPLE_drip:
14707       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14708       break;
14709
14710     case SAMPLE_push:
14711       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14712       break;
14713
14714     case SAMPLE_dirt:
14715       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14716       break;
14717
14718     case SAMPLE_acid:
14719       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14720       break;
14721
14722     case SAMPLE_ball:
14723       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14724       break;
14725
14726     case SAMPLE_grow:
14727       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14728       break;
14729
14730     case SAMPLE_wonder:
14731       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14732       break;
14733
14734     case SAMPLE_door:
14735       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14736       break;
14737
14738     case SAMPLE_exit_open:
14739       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14740       break;
14741
14742     case SAMPLE_exit_leave:
14743       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14744       break;
14745
14746     case SAMPLE_dynamite:
14747       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14748       break;
14749
14750     case SAMPLE_tick:
14751       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14752       break;
14753
14754     case SAMPLE_press:
14755       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14756       break;
14757
14758     case SAMPLE_wheel:
14759       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14760       break;
14761
14762     case SAMPLE_boom:
14763       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14764       break;
14765
14766     case SAMPLE_die:
14767       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14768       break;
14769
14770     case SAMPLE_time:
14771       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14772       break;
14773
14774     default:
14775       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14776       break;
14777   }
14778 }
14779
14780 #if 0
14781 void ChangeTime(int value)
14782 {
14783   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
14784
14785   *time += value;
14786
14787   /* EMC game engine uses value from time counter of RND game engine */
14788   level.native_em_level->lev->time = *time;
14789
14790   DrawGameValue_Time(*time);
14791 }
14792
14793 void RaiseScore(int value)
14794 {
14795   /* EMC game engine and RND game engine have separate score counters */
14796   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
14797                 &level.native_em_level->lev->score : &local_player->score);
14798
14799   *score += value;
14800
14801   DrawGameValue_Score(*score);
14802 }
14803 #endif
14804
14805 void RaiseScore(int value)
14806 {
14807   local_player->score += value;
14808
14809 #if 1
14810   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14811
14812   DisplayGameControlValues();
14813 #else
14814   DrawGameValue_Score(local_player->score);
14815 #endif
14816 }
14817
14818 void RaiseScoreElement(int element)
14819 {
14820   switch (element)
14821   {
14822     case EL_EMERALD:
14823     case EL_BD_DIAMOND:
14824     case EL_EMERALD_YELLOW:
14825     case EL_EMERALD_RED:
14826     case EL_EMERALD_PURPLE:
14827     case EL_SP_INFOTRON:
14828       RaiseScore(level.score[SC_EMERALD]);
14829       break;
14830     case EL_DIAMOND:
14831       RaiseScore(level.score[SC_DIAMOND]);
14832       break;
14833     case EL_CRYSTAL:
14834       RaiseScore(level.score[SC_CRYSTAL]);
14835       break;
14836     case EL_PEARL:
14837       RaiseScore(level.score[SC_PEARL]);
14838       break;
14839     case EL_BUG:
14840     case EL_BD_BUTTERFLY:
14841     case EL_SP_ELECTRON:
14842       RaiseScore(level.score[SC_BUG]);
14843       break;
14844     case EL_SPACESHIP:
14845     case EL_BD_FIREFLY:
14846     case EL_SP_SNIKSNAK:
14847       RaiseScore(level.score[SC_SPACESHIP]);
14848       break;
14849     case EL_YAMYAM:
14850     case EL_DARK_YAMYAM:
14851       RaiseScore(level.score[SC_YAMYAM]);
14852       break;
14853     case EL_ROBOT:
14854       RaiseScore(level.score[SC_ROBOT]);
14855       break;
14856     case EL_PACMAN:
14857       RaiseScore(level.score[SC_PACMAN]);
14858       break;
14859     case EL_NUT:
14860       RaiseScore(level.score[SC_NUT]);
14861       break;
14862     case EL_DYNAMITE:
14863     case EL_EM_DYNAMITE:
14864     case EL_SP_DISK_RED:
14865     case EL_DYNABOMB_INCREASE_NUMBER:
14866     case EL_DYNABOMB_INCREASE_SIZE:
14867     case EL_DYNABOMB_INCREASE_POWER:
14868       RaiseScore(level.score[SC_DYNAMITE]);
14869       break;
14870     case EL_SHIELD_NORMAL:
14871     case EL_SHIELD_DEADLY:
14872       RaiseScore(level.score[SC_SHIELD]);
14873       break;
14874     case EL_EXTRA_TIME:
14875       RaiseScore(level.extra_time_score);
14876       break;
14877     case EL_KEY_1:
14878     case EL_KEY_2:
14879     case EL_KEY_3:
14880     case EL_KEY_4:
14881     case EL_EM_KEY_1:
14882     case EL_EM_KEY_2:
14883     case EL_EM_KEY_3:
14884     case EL_EM_KEY_4:
14885     case EL_EMC_KEY_5:
14886     case EL_EMC_KEY_6:
14887     case EL_EMC_KEY_7:
14888     case EL_EMC_KEY_8:
14889     case EL_DC_KEY_WHITE:
14890       RaiseScore(level.score[SC_KEY]);
14891       break;
14892     default:
14893       RaiseScore(element_info[element].collect_score);
14894       break;
14895   }
14896 }
14897
14898 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14899 {
14900   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14901   {
14902 #if defined(NETWORK_AVALIABLE)
14903     if (options.network)
14904       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14905     else
14906 #endif
14907     {
14908       if (quick_quit)
14909       {
14910 #if 1
14911
14912 #if 1
14913         FadeSkipNextFadeIn();
14914 #else
14915         fading = fading_none;
14916 #endif
14917
14918 #else
14919         OpenDoor(DOOR_CLOSE_1);
14920 #endif
14921
14922         game_status = GAME_MODE_MAIN;
14923
14924 #if 1
14925         DrawAndFadeInMainMenu(REDRAW_FIELD);
14926 #else
14927         DrawMainMenu();
14928 #endif
14929       }
14930       else
14931       {
14932 #if 0
14933         FadeOut(REDRAW_FIELD);
14934 #endif
14935
14936         game_status = GAME_MODE_MAIN;
14937
14938         DrawAndFadeInMainMenu(REDRAW_FIELD);
14939       }
14940     }
14941   }
14942   else          /* continue playing the game */
14943   {
14944     if (tape.playing && tape.deactivate_display)
14945       TapeDeactivateDisplayOff(TRUE);
14946
14947     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14948
14949     if (tape.playing && tape.deactivate_display)
14950       TapeDeactivateDisplayOn();
14951   }
14952 }
14953
14954 void RequestQuitGame(boolean ask_if_really_quit)
14955 {
14956   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14957   boolean skip_request = AllPlayersGone || quick_quit;
14958
14959   RequestQuitGameExt(skip_request, quick_quit,
14960                      "Do you really want to quit the game ?");
14961 }
14962
14963
14964 /* ------------------------------------------------------------------------- */
14965 /* random generator functions                                                */
14966 /* ------------------------------------------------------------------------- */
14967
14968 unsigned int InitEngineRandom_RND(long seed)
14969 {
14970   game.num_random_calls = 0;
14971
14972 #if 0
14973   unsigned int rnd_seed = InitEngineRandom(seed);
14974
14975   printf("::: START RND: %d\n", rnd_seed);
14976
14977   return rnd_seed;
14978 #else
14979
14980   return InitEngineRandom(seed);
14981
14982 #endif
14983
14984 }
14985
14986 unsigned int RND(int max)
14987 {
14988   if (max > 0)
14989   {
14990     game.num_random_calls++;
14991
14992     return GetEngineRandom(max);
14993   }
14994
14995   return 0;
14996 }
14997
14998
14999 /* ------------------------------------------------------------------------- */
15000 /* game engine snapshot handling functions                                   */
15001 /* ------------------------------------------------------------------------- */
15002
15003 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15004
15005 struct EngineSnapshotInfo
15006 {
15007   /* runtime values for custom element collect score */
15008   int collect_score[NUM_CUSTOM_ELEMENTS];
15009
15010   /* runtime values for group element choice position */
15011   int choice_pos[NUM_GROUP_ELEMENTS];
15012
15013   /* runtime values for belt position animations */
15014   int belt_graphic[4 * NUM_BELT_PARTS];
15015   int belt_anim_mode[4 * NUM_BELT_PARTS];
15016 };
15017
15018 struct EngineSnapshotNodeInfo
15019 {
15020   void *buffer_orig;
15021   void *buffer_copy;
15022   int size;
15023 };
15024
15025 static struct EngineSnapshotInfo engine_snapshot_rnd;
15026 static ListNode *engine_snapshot_list = NULL;
15027 static char *snapshot_level_identifier = NULL;
15028 static int snapshot_level_nr = -1;
15029
15030 void FreeEngineSnapshot()
15031 {
15032   while (engine_snapshot_list != NULL)
15033     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15034                        checked_free);
15035
15036   setString(&snapshot_level_identifier, NULL);
15037   snapshot_level_nr = -1;
15038 }
15039
15040 static void SaveEngineSnapshotValues_RND()
15041 {
15042   static int belt_base_active_element[4] =
15043   {
15044     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15045     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15046     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15047     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15048   };
15049   int i, j;
15050
15051   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15052   {
15053     int element = EL_CUSTOM_START + i;
15054
15055     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15056   }
15057
15058   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15059   {
15060     int element = EL_GROUP_START + i;
15061
15062     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15063   }
15064
15065   for (i = 0; i < 4; i++)
15066   {
15067     for (j = 0; j < NUM_BELT_PARTS; j++)
15068     {
15069       int element = belt_base_active_element[i] + j;
15070       int graphic = el2img(element);
15071       int anim_mode = graphic_info[graphic].anim_mode;
15072
15073       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15074       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15075     }
15076   }
15077 }
15078
15079 static void LoadEngineSnapshotValues_RND()
15080 {
15081   unsigned long num_random_calls = game.num_random_calls;
15082   int i, j;
15083
15084   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15085   {
15086     int element = EL_CUSTOM_START + i;
15087
15088     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15089   }
15090
15091   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15092   {
15093     int element = EL_GROUP_START + i;
15094
15095     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15096   }
15097
15098   for (i = 0; i < 4; i++)
15099   {
15100     for (j = 0; j < NUM_BELT_PARTS; j++)
15101     {
15102       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15103       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15104
15105       graphic_info[graphic].anim_mode = anim_mode;
15106     }
15107   }
15108
15109   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15110   {
15111     InitRND(tape.random_seed);
15112     for (i = 0; i < num_random_calls; i++)
15113       RND(1);
15114   }
15115
15116   if (game.num_random_calls != num_random_calls)
15117   {
15118     Error(ERR_INFO, "number of random calls out of sync");
15119     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15120     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15121     Error(ERR_EXIT, "this should not happen -- please debug");
15122   }
15123 }
15124
15125 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15126 {
15127   struct EngineSnapshotNodeInfo *bi =
15128     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15129
15130   bi->buffer_orig = buffer;
15131   bi->buffer_copy = checked_malloc(size);
15132   bi->size = size;
15133
15134   memcpy(bi->buffer_copy, buffer, size);
15135
15136   addNodeToList(&engine_snapshot_list, NULL, bi);
15137 }
15138
15139 void SaveEngineSnapshot()
15140 {
15141   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15142
15143   if (level_editor_test_game)   /* do not save snapshots from editor */
15144     return;
15145
15146   /* copy some special values to a structure better suited for the snapshot */
15147
15148   SaveEngineSnapshotValues_RND();
15149   SaveEngineSnapshotValues_EM();
15150
15151   /* save values stored in special snapshot structure */
15152
15153   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15154   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15155
15156   /* save further RND engine values */
15157
15158   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15159   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15160   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15161
15162   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15163   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15164   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15165   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15166
15167   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15168   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15169   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15170   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15171   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15172
15173   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15174   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15175   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15176
15177   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15178
15179   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15180
15181   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15182   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15183
15184   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15185   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15186   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15187   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15188   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15189   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15190   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15191   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15192   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15193   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15194   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15195   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15196   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15197   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15198   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15199   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15200   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15201   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15202
15203   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15204   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15205
15206   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15207   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15208   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15209
15210   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15211   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15212
15213   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15214   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15215   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15216   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15217   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15218
15219   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15220   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15221
15222   /* save level identification information */
15223
15224   setString(&snapshot_level_identifier, leveldir_current->identifier);
15225   snapshot_level_nr = level_nr;
15226
15227 #if 0
15228   ListNode *node = engine_snapshot_list;
15229   int num_bytes = 0;
15230
15231   while (node != NULL)
15232   {
15233     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15234
15235     node = node->next;
15236   }
15237
15238   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15239 #endif
15240 }
15241
15242 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15243 {
15244   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15245 }
15246
15247 void LoadEngineSnapshot()
15248 {
15249   ListNode *node = engine_snapshot_list;
15250
15251   if (engine_snapshot_list == NULL)
15252     return;
15253
15254   while (node != NULL)
15255   {
15256     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15257
15258     node = node->next;
15259   }
15260
15261   /* restore special values from snapshot structure */
15262
15263   LoadEngineSnapshotValues_RND();
15264   LoadEngineSnapshotValues_EM();
15265 }
15266
15267 boolean CheckEngineSnapshot()
15268 {
15269   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15270           snapshot_level_nr == level_nr);
15271 }
15272
15273
15274 /* ---------- new game button stuff ---------------------------------------- */
15275
15276 /* graphic position values for game buttons */
15277 #define GAME_BUTTON_XSIZE       30
15278 #define GAME_BUTTON_YSIZE       30
15279 #define GAME_BUTTON_XPOS        5
15280 #define GAME_BUTTON_YPOS        215
15281 #define SOUND_BUTTON_XPOS       5
15282 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15283
15284 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15285 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15286 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15287 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15288 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15289 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15290
15291 static struct
15292 {
15293   int *x, *y;
15294   int gd_x, gd_y;
15295   int gadget_id;
15296   char *infotext;
15297 } gamebutton_info[NUM_GAME_BUTTONS] =
15298 {
15299 #if 1
15300   {
15301     &game.button.stop.x,        &game.button.stop.y,
15302     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15303     GAME_CTRL_ID_STOP,
15304     "stop game"
15305   },
15306   {
15307     &game.button.pause.x,       &game.button.pause.y,
15308     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15309     GAME_CTRL_ID_PAUSE,
15310     "pause game"
15311   },
15312   {
15313     &game.button.play.x,        &game.button.play.y,
15314     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15315     GAME_CTRL_ID_PLAY,
15316     "play game"
15317   },
15318   {
15319     &game.button.sound_music.x, &game.button.sound_music.y,
15320     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15321     SOUND_CTRL_ID_MUSIC,
15322     "background music on/off"
15323   },
15324   {
15325     &game.button.sound_loops.x, &game.button.sound_loops.y,
15326     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15327     SOUND_CTRL_ID_LOOPS,
15328     "sound loops on/off"
15329   },
15330   {
15331     &game.button.sound_simple.x,&game.button.sound_simple.y,
15332     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15333     SOUND_CTRL_ID_SIMPLE,
15334     "normal sounds on/off"
15335   }
15336 #else
15337   {
15338     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15339     GAME_CTRL_ID_STOP,
15340     "stop game"
15341   },
15342   {
15343     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15344     GAME_CTRL_ID_PAUSE,
15345     "pause game"
15346   },
15347   {
15348     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15349     GAME_CTRL_ID_PLAY,
15350     "play game"
15351   },
15352   {
15353     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15354     SOUND_CTRL_ID_MUSIC,
15355     "background music on/off"
15356   },
15357   {
15358     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15359     SOUND_CTRL_ID_LOOPS,
15360     "sound loops on/off"
15361   },
15362   {
15363     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15364     SOUND_CTRL_ID_SIMPLE,
15365     "normal sounds on/off"
15366   }
15367 #endif
15368 };
15369
15370 void CreateGameButtons()
15371 {
15372   int i;
15373
15374   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15375   {
15376     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15377     struct GadgetInfo *gi;
15378     int button_type;
15379     boolean checked;
15380     unsigned long event_mask;
15381     int x, y;
15382     int gd_xoffset, gd_yoffset;
15383     int gd_x1, gd_x2, gd_y1, gd_y2;
15384     int id = i;
15385
15386     x = DX + *gamebutton_info[i].x;
15387     y = DY + *gamebutton_info[i].y;
15388     gd_xoffset = gamebutton_info[i].gd_x;
15389     gd_yoffset = gamebutton_info[i].gd_y;
15390     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15391     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15392
15393     if (id == GAME_CTRL_ID_STOP ||
15394         id == GAME_CTRL_ID_PAUSE ||
15395         id == GAME_CTRL_ID_PLAY)
15396     {
15397       button_type = GD_TYPE_NORMAL_BUTTON;
15398       checked = FALSE;
15399       event_mask = GD_EVENT_RELEASED;
15400       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15401       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15402     }
15403     else
15404     {
15405       button_type = GD_TYPE_CHECK_BUTTON;
15406       checked =
15407         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15408          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15409          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15410       event_mask = GD_EVENT_PRESSED;
15411       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15412       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15413     }
15414
15415     gi = CreateGadget(GDI_CUSTOM_ID, id,
15416                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15417 #if 1
15418                       GDI_X, x,
15419                       GDI_Y, y,
15420 #else
15421                       GDI_X, DX + gd_xoffset,
15422                       GDI_Y, DY + gd_yoffset,
15423 #endif
15424                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15425                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15426                       GDI_TYPE, button_type,
15427                       GDI_STATE, GD_BUTTON_UNPRESSED,
15428                       GDI_CHECKED, checked,
15429                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15430                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15431                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15432                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15433                       GDI_EVENT_MASK, event_mask,
15434                       GDI_CALLBACK_ACTION, HandleGameButtons,
15435                       GDI_END);
15436
15437     if (gi == NULL)
15438       Error(ERR_EXIT, "cannot create gadget");
15439
15440     game_gadget[id] = gi;
15441   }
15442 }
15443
15444 void FreeGameButtons()
15445 {
15446   int i;
15447
15448   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15449     FreeGadget(game_gadget[i]);
15450 }
15451
15452 static void MapGameButtons()
15453 {
15454   int i;
15455
15456   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15457     MapGadget(game_gadget[i]);
15458 }
15459
15460 void UnmapGameButtons()
15461 {
15462   int i;
15463
15464   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15465     UnmapGadget(game_gadget[i]);
15466 }
15467
15468 static void HandleGameButtons(struct GadgetInfo *gi)
15469 {
15470   int id = gi->custom_id;
15471
15472   if (game_status != GAME_MODE_PLAYING)
15473     return;
15474
15475   switch (id)
15476   {
15477     case GAME_CTRL_ID_STOP:
15478       if (tape.playing)
15479         TapeStop();
15480       else
15481         RequestQuitGame(TRUE);
15482       break;
15483
15484     case GAME_CTRL_ID_PAUSE:
15485       if (options.network)
15486       {
15487 #if defined(NETWORK_AVALIABLE)
15488         if (tape.pausing)
15489           SendToServer_ContinuePlaying();
15490         else
15491           SendToServer_PausePlaying();
15492 #endif
15493       }
15494       else
15495         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15496       break;
15497
15498     case GAME_CTRL_ID_PLAY:
15499       if (tape.pausing)
15500       {
15501 #if defined(NETWORK_AVALIABLE)
15502         if (options.network)
15503           SendToServer_ContinuePlaying();
15504         else
15505 #endif
15506         {
15507           tape.pausing = FALSE;
15508           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15509         }
15510       }
15511       break;
15512
15513     case SOUND_CTRL_ID_MUSIC:
15514       if (setup.sound_music)
15515       { 
15516         setup.sound_music = FALSE;
15517         FadeMusic();
15518       }
15519       else if (audio.music_available)
15520       { 
15521         setup.sound = setup.sound_music = TRUE;
15522
15523         SetAudioMode(setup.sound);
15524
15525         PlayLevelMusic();
15526       }
15527       break;
15528
15529     case SOUND_CTRL_ID_LOOPS:
15530       if (setup.sound_loops)
15531         setup.sound_loops = FALSE;
15532       else if (audio.loops_available)
15533       {
15534         setup.sound = setup.sound_loops = TRUE;
15535         SetAudioMode(setup.sound);
15536       }
15537       break;
15538
15539     case SOUND_CTRL_ID_SIMPLE:
15540       if (setup.sound_simple)
15541         setup.sound_simple = FALSE;
15542       else if (audio.sound_available)
15543       {
15544         setup.sound = setup.sound_simple = TRUE;
15545         SetAudioMode(setup.sound);
15546       }
15547       break;
15548
15549     default:
15550       break;
15551   }
15552 }