rnd-20070413-1-src
[rocksndiamonds.git] / src / game.c
1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13
14 #include "libgame/libgame.h"
15
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE     FALSE
26
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF                   (                         1)
29
30 #define USE_NEW_SP_SLIPPERY             (USE_NEW_STUFF          * 1)
31 #define USE_NEW_CUSTOM_VALUE            (USE_NEW_STUFF          * 1)
32 #define USE_NEW_PLAYER_ANIM             (USE_NEW_STUFF          * 1)
33 #define USE_NEW_ALL_SLIPPERY            (USE_NEW_STUFF          * 1)
34 #define USE_NEW_PLAYER_SPEED            (USE_NEW_STUFF          * 1)
35 #define USE_NEW_DELAYED_ACTION          (USE_NEW_STUFF          * 1)
36 #define USE_NEW_SNAP_DELAY              (USE_NEW_STUFF          * 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME   (USE_NEW_STUFF          * 1)
39 #define USE_FIXED_DONT_RUN_INTO         (USE_NEW_STUFF          * 1)
40 #define USE_NEW_SPRING_BUMPER           (USE_NEW_STUFF          * 1)
41 #define USE_STOP_CHANGED_ELEMENTS       (USE_NEW_STUFF          * 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX     (USE_NEW_STUFF          * 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING     (USE_NEW_STUFF          * 1)
44 #define USE_GFX_RESET_GFX_ANIMATION     (USE_NEW_STUFF          * 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES    (USE_NEW_STUFF          * 1)
46 #define USE_PLAYER_GRAVITY              (USE_NEW_STUFF          * 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX    (USE_NEW_STUFF          * 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX    (USE_NEW_STUFF          * 0)
49
50 #define USE_QUICKSAND_IMPACT_BUGFIX     (USE_NEW_STUFF          * 0)
51
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF          * 1)
53
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX    (USE_NEW_STUFF          * 1)
55
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING  (USE_NEW_STUFF          * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK    (USE_NEW_STUFF          * 1)
58
59 #define USE_FIX_KILLED_BY_NON_WALKABLE  (USE_NEW_STUFF          * 1)
60 #define USE_FIX_IMPACT_COLLISION        (USE_NEW_STUFF          * 1)
61
62 #define USE_GFX_RESET_WHEN_NOT_MOVING   (USE_NEW_STUFF          * 1)
63
64
65 /* for DigField() */
66 #define DF_NO_PUSH              0
67 #define DF_DIG                  1
68 #define DF_SNAP                 2
69
70 /* for MovePlayer() */
71 #define MP_NO_ACTION            0
72 #define MP_MOVING               1
73 #define MP_ACTION               2
74 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
75
76 /* for ScrollPlayer() */
77 #define SCROLL_INIT             0
78 #define SCROLL_GO_ON            1
79
80 /* for Bang()/Explode() */
81 #define EX_PHASE_START          0
82 #define EX_TYPE_NONE            0
83 #define EX_TYPE_NORMAL          (1 << 0)
84 #define EX_TYPE_CENTER          (1 << 1)
85 #define EX_TYPE_BORDER          (1 << 2)
86 #define EX_TYPE_CROSS           (1 << 3)
87 #define EX_TYPE_DYNA            (1 << 4)
88 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
89
90 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
91 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
92 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
93 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
94
95 /* special positions in the game control window (relative to control window) */
96 #define XX_LEVEL1               (PANEL_XPOS(game.panel.level))
97 #define XX_LEVEL2               (PANEL_XPOS(game.panel.level) - 1)
98 #define XX_LEVEL                (PANEL_XPOS(game.panel.level))
99 #define YY_LEVEL                (PANEL_YPOS(game.panel.level))
100 #define XX_EMERALDS             (PANEL_XPOS(game.panel.gems))
101 #define YY_EMERALDS             (PANEL_YPOS(game.panel.gems))
102 #define XX_DYNAMITE             (PANEL_XPOS(game.panel.inventory))
103 #define YY_DYNAMITE             (PANEL_YPOS(game.panel.inventory))
104 #define XX_KEYS                 (PANEL_XPOS(game.panel.keys))
105 #define YY_KEYS                 (PANEL_YPOS(game.panel.keys))
106 #define XX_SCORE                (PANEL_XPOS(game.panel.score))
107 #define YY_SCORE                (PANEL_YPOS(game.panel.score))
108 #define XX_TIME1                (PANEL_XPOS(game.panel.time))
109 #define XX_TIME2                (PANEL_XPOS(game.panel.time) + 1)
110 #define XX_TIME                 (PANEL_XPOS(game.panel.time))
111 #define YY_TIME                 (PANEL_YPOS(game.panel.time))
112
113 /* special positions in the game control window (relative to main window) */
114 #define DX_LEVEL1               (DX + XX_LEVEL1)
115 #define DX_LEVEL2               (DX + XX_LEVEL2)
116 #define DX_LEVEL                (DX + XX_LEVEL)
117 #define DY_LEVEL                (DY + YY_LEVEL)
118 #define DX_EMERALDS             (DX + XX_EMERALDS)
119 #define DY_EMERALDS             (DY + YY_EMERALDS)
120 #define DX_DYNAMITE             (DX + XX_DYNAMITE)
121 #define DY_DYNAMITE             (DY + YY_DYNAMITE)
122 #define DX_KEYS                 (DX + XX_KEYS)
123 #define DY_KEYS                 (DY + YY_KEYS)
124 #define DX_SCORE                (DX + XX_SCORE)
125 #define DY_SCORE                (DY + YY_SCORE)
126 #define DX_TIME1                (DX + XX_TIME1)
127 #define DX_TIME2                (DX + XX_TIME2)
128 #define DX_TIME                 (DX + XX_TIME)
129 #define DY_TIME                 (DY + YY_TIME)
130
131 #if 1
132 /* game panel display and control definitions */
133
134 #define GAME_PANEL_LEVEL_NUMBER                 0
135 #define GAME_PANEL_GEMS                         1
136 #define GAME_PANEL_INVENTORY_COUNT              2
137 #define GAME_PANEL_INVENTORY_FIRST_1            3
138 #define GAME_PANEL_INVENTORY_FIRST_2            4
139 #define GAME_PANEL_INVENTORY_FIRST_3            5
140 #define GAME_PANEL_INVENTORY_FIRST_4            6
141 #define GAME_PANEL_INVENTORY_FIRST_5            7
142 #define GAME_PANEL_INVENTORY_FIRST_6            8
143 #define GAME_PANEL_INVENTORY_FIRST_7            9
144 #define GAME_PANEL_INVENTORY_FIRST_8            10
145 #define GAME_PANEL_INVENTORY_LAST_1             11
146 #define GAME_PANEL_INVENTORY_LAST_2             12
147 #define GAME_PANEL_INVENTORY_LAST_3             13
148 #define GAME_PANEL_INVENTORY_LAST_4             14
149 #define GAME_PANEL_INVENTORY_LAST_5             15
150 #define GAME_PANEL_INVENTORY_LAST_6             16
151 #define GAME_PANEL_INVENTORY_LAST_7             17
152 #define GAME_PANEL_INVENTORY_LAST_8             18
153 #define GAME_PANEL_KEY_1                        19
154 #define GAME_PANEL_KEY_2                        20
155 #define GAME_PANEL_KEY_3                        21
156 #define GAME_PANEL_KEY_4                        22
157 #define GAME_PANEL_KEY_5                        23
158 #define GAME_PANEL_KEY_6                        24
159 #define GAME_PANEL_KEY_7                        25
160 #define GAME_PANEL_KEY_8                        26
161 #define GAME_PANEL_KEY_WHITE                    27
162 #define GAME_PANEL_KEY_WHITE_COUNT              28
163 #define GAME_PANEL_SCORE                        29
164 #define GAME_PANEL_TIME                         30
165 #define GAME_PANEL_TIME_HH                      31
166 #define GAME_PANEL_TIME_MM                      32
167 #define GAME_PANEL_TIME_SS                      33
168 #define GAME_PANEL_SHIELD_NORMAL                34
169 #define GAME_PANEL_SHIELD_NORMAL_TIME           35
170 #define GAME_PANEL_SHIELD_DEADLY                36
171 #define GAME_PANEL_SHIELD_DEADLY_TIME           37
172 #define GAME_PANEL_EXIT                         38
173 #define GAME_PANEL_EMC_MAGIC_BALL               39
174 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        40
175 #define GAME_PANEL_LIGHT_SWITCH                 41
176 #define GAME_PANEL_LIGHT_SWITCH_TIME            42
177 #define GAME_PANEL_TIMEGATE_SWITCH              43
178 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         44
179 #define GAME_PANEL_SWITCHGATE_SWITCH            45
180 #define GAME_PANEL_EMC_LENSES                   46
181 #define GAME_PANEL_EMC_LENSES_TIME              47
182 #define GAME_PANEL_EMC_MAGNIFIER                48
183 #define GAME_PANEL_EMC_MAGNIFIER_TIME           49
184 #define GAME_PANEL_BALLOON_SWITCH               50
185 #define GAME_PANEL_DYNABOMB_NUMBER              51
186 #define GAME_PANEL_DYNABOMB_SIZE                52
187 #define GAME_PANEL_DYNABOMB_POWER               53
188 #define GAME_PANEL_PENGUINS                     54
189 #define GAME_PANEL_SOKOBAN_OBJECTS              55
190 #define GAME_PANEL_SOKOBAN_FIELDS               56
191 #define GAME_PANEL_ROBOT_WHEEL                  57
192 #define GAME_PANEL_CONVEYOR_BELT_1              58
193 #define GAME_PANEL_CONVEYOR_BELT_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_ELEMENT_1                    69
204 #define GAME_PANEL_ELEMENT_2                    70
205 #define GAME_PANEL_ELEMENT_3                    71
206 #define GAME_PANEL_ELEMENT_4                    72
207 #define GAME_PANEL_ELEMENT_5                    73
208 #define GAME_PANEL_ELEMENT_6                    74
209 #define GAME_PANEL_ELEMENT_7                    75
210 #define GAME_PANEL_ELEMENT_8                    76
211 #define GAME_PANEL_ELEMENT_COUNT_1              77
212 #define GAME_PANEL_ELEMENT_COUNT_2              78
213 #define GAME_PANEL_ELEMENT_COUNT_3              79
214 #define GAME_PANEL_ELEMENT_COUNT_4              80
215 #define GAME_PANEL_ELEMENT_COUNT_5              81
216 #define GAME_PANEL_ELEMENT_COUNT_6              82
217 #define GAME_PANEL_ELEMENT_COUNT_7              83
218 #define GAME_PANEL_ELEMENT_COUNT_8              84
219 #define GAME_PANEL_CE_SCORE_1                   85
220 #define GAME_PANEL_CE_SCORE_2                   86
221 #define GAME_PANEL_CE_SCORE_3                   87
222 #define GAME_PANEL_CE_SCORE_4                   88
223 #define GAME_PANEL_CE_SCORE_5                   89
224 #define GAME_PANEL_CE_SCORE_6                   90
225 #define GAME_PANEL_CE_SCORE_7                   91
226 #define GAME_PANEL_CE_SCORE_8                   92
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT           93
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT           94
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT           95
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT           96
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT           97
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT           98
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT           99
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT           100
235 #define GAME_PANEL_PLAYER_NAME                  101
236 #define GAME_PANEL_LEVEL_NAME                   102
237 #define GAME_PANEL_LEVEL_AUTHOR                 103
238
239 #define NUM_GAME_PANEL_CONTROLS                 104
240
241 struct GamePanelOrderInfo
242 {
243   int nr;
244   int sort_priority;
245 };
246
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
248
249 struct GamePanelControlInfo
250 {
251   int nr;
252
253   struct TextPosInfo *pos;
254   int type;
255
256   int value, last_value;
257   int frame, last_frame;
258   int gfx_frame;
259   int gfx_random;
260 };
261
262 static struct GamePanelControlInfo game_panel_controls[] =
263 {
264   {
265     GAME_PANEL_LEVEL_NUMBER,
266     &game.panel.level_number,
267     TYPE_INTEGER,
268   },
269   {
270     GAME_PANEL_GEMS,
271     &game.panel.gems,
272     TYPE_INTEGER,
273   },
274   {
275     GAME_PANEL_INVENTORY_COUNT,
276     &game.panel.inventory_count,
277     TYPE_INTEGER,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_1,
281     &game.panel.inventory_first[0],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_2,
286     &game.panel.inventory_first[1],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_3,
291     &game.panel.inventory_first[2],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_FIRST_4,
296     &game.panel.inventory_first[3],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_FIRST_5,
301     &game.panel.inventory_first[4],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_FIRST_6,
306     &game.panel.inventory_first[5],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_FIRST_7,
311     &game.panel.inventory_first[6],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_8,
316     &game.panel.inventory_first[7],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_1,
321     &game.panel.inventory_last[0],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_2,
326     &game.panel.inventory_last[1],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_3,
331     &game.panel.inventory_last[2],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_LAST_4,
336     &game.panel.inventory_last[3],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_LAST_5,
341     &game.panel.inventory_last[4],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_LAST_6,
346     &game.panel.inventory_last[5],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_LAST_7,
351     &game.panel.inventory_last[6],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_8,
356     &game.panel.inventory_last[7],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_1,
361     &game.panel.key[0],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_2,
366     &game.panel.key[1],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_3,
371     &game.panel.key[2],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_4,
376     &game.panel.key[3],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_5,
381     &game.panel.key[4],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_KEY_6,
386     &game.panel.key[5],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_KEY_7,
391     &game.panel.key[6],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_8,
396     &game.panel.key[7],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_WHITE,
401     &game.panel.key_white,
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_WHITE_COUNT,
406     &game.panel.key_white_count,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SCORE,
411     &game.panel.score,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_PANEL_TIME,
416     &game.panel.time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_TIME_HH,
421     &game.panel.time_hh,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_PANEL_TIME_MM,
426     &game.panel.time_mm,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_TIME_SS,
431     &game.panel.time_ss,
432     TYPE_INTEGER,
433   },
434   {
435     GAME_PANEL_SHIELD_NORMAL,
436     &game.panel.shield_normal,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_SHIELD_NORMAL_TIME,
441     &game.panel.shield_normal_time,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_SHIELD_DEADLY,
446     &game.panel.shield_deadly,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_SHIELD_DEADLY_TIME,
451     &game.panel.shield_deadly_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_EXIT,
456     &game.panel.exit,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_EMC_MAGIC_BALL,
461     &game.panel.emc_magic_ball,
462     TYPE_ELEMENT,
463   },
464   {
465     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
466     &game.panel.emc_magic_ball_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_LIGHT_SWITCH,
471     &game.panel.light_switch,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_LIGHT_SWITCH_TIME,
476     &game.panel.light_switch_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_TIMEGATE_SWITCH,
481     &game.panel.timegate_switch,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_TIMEGATE_SWITCH_TIME,
486     &game.panel.timegate_switch_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_SWITCHGATE_SWITCH,
491     &game.panel.switchgate_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_EMC_LENSES,
496     &game.panel.emc_lenses,
497     TYPE_ELEMENT,
498   },
499   {
500     GAME_PANEL_EMC_LENSES_TIME,
501     &game.panel.emc_lenses_time,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_EMC_MAGNIFIER,
506     &game.panel.emc_magnifier,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_EMC_MAGNIFIER_TIME,
511     &game.panel.emc_magnifier_time,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_BALLOON_SWITCH,
516     &game.panel.balloon_switch,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_DYNABOMB_NUMBER,
521     &game.panel.dynabomb_number,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_DYNABOMB_SIZE,
526     &game.panel.dynabomb_size,
527     TYPE_INTEGER,
528   },
529   {
530     GAME_PANEL_DYNABOMB_POWER,
531     &game.panel.dynabomb_power,
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_PENGUINS,
536     &game.panel.penguins,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_PANEL_SOKOBAN_OBJECTS,
541     &game.panel.sokoban_objects,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_SOKOBAN_FIELDS,
546     &game.panel.sokoban_fields,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_ROBOT_WHEEL,
551     &game.panel.robot_wheel,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_1,
556     &game.panel.conveyor_belt[0],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_2,
561     &game.panel.conveyor_belt[1],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_3,
566     &game.panel.conveyor_belt[2],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_CONVEYOR_BELT_4,
571     &game.panel.conveyor_belt[3],
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
576     &game.panel.conveyor_belt_switch[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
581     &game.panel.conveyor_belt_switch[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
586     &game.panel.conveyor_belt_switch[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
591     &game.panel.conveyor_belt_switch[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_MAGIC_WALL,
596     &game.panel.magic_wall,
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_MAGIC_WALL_TIME,
601     &game.panel.magic_wall_time,
602     TYPE_INTEGER,
603   },
604   {
605     GAME_PANEL_GRAVITY_STATE,
606     &game.panel.gravity_state,
607     TYPE_STRING,
608   },
609   {
610     GAME_PANEL_ELEMENT_1,
611     &game.panel.element[0],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_ELEMENT_2,
616     &game.panel.element[1],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_ELEMENT_3,
621     &game.panel.element[2],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_4,
626     &game.panel.element[3],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_5,
631     &game.panel.element[4],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_6,
636     &game.panel.element[5],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_7,
641     &game.panel.element[6],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_8,
646     &game.panel.element[7],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_COUNT_1,
651     &game.panel.element_count[0],
652     TYPE_INTEGER,
653   },
654   {
655     GAME_PANEL_ELEMENT_COUNT_2,
656     &game.panel.element_count[1],
657     TYPE_INTEGER,
658   },
659   {
660     GAME_PANEL_ELEMENT_COUNT_3,
661     &game.panel.element_count[2],
662     TYPE_INTEGER,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_4,
666     &game.panel.element_count[3],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_5,
671     &game.panel.element_count[4],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_6,
676     &game.panel.element_count[5],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_7,
681     &game.panel.element_count[6],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_8,
686     &game.panel.element_count[7],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_CE_SCORE_1,
691     &game.panel.ce_score[0],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_CE_SCORE_2,
696     &game.panel.ce_score[1],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_CE_SCORE_3,
701     &game.panel.ce_score[2],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_4,
706     &game.panel.ce_score[3],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_5,
711     &game.panel.ce_score[4],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_6,
716     &game.panel.ce_score[5],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_7,
721     &game.panel.ce_score[6],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_8,
726     &game.panel.ce_score[7],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_1_ELEMENT,
731     &game.panel.ce_score_element[0],
732     TYPE_ELEMENT,
733   },
734   {
735     GAME_PANEL_CE_SCORE_2_ELEMENT,
736     &game.panel.ce_score_element[1],
737     TYPE_ELEMENT,
738   },
739   {
740     GAME_PANEL_CE_SCORE_3_ELEMENT,
741     &game.panel.ce_score_element[2],
742     TYPE_ELEMENT,
743   },
744   {
745     GAME_PANEL_CE_SCORE_4_ELEMENT,
746     &game.panel.ce_score_element[3],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_5_ELEMENT,
751     &game.panel.ce_score_element[4],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_6_ELEMENT,
756     &game.panel.ce_score_element[5],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_7_ELEMENT,
761     &game.panel.ce_score_element[6],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_8_ELEMENT,
766     &game.panel.ce_score_element[7],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_PLAYER_NAME,
771     &game.panel.player_name,
772     TYPE_STRING,
773   },
774   {
775     GAME_PANEL_LEVEL_NAME,
776     &game.panel.level_name,
777     TYPE_STRING,
778   },
779   {
780     GAME_PANEL_LEVEL_AUTHOR,
781     &game.panel.level_author,
782     TYPE_STRING,
783   },
784
785   {
786     -1,
787     NULL,
788     -1,
789   }
790 };
791 #endif
792
793
794 /* values for delayed check of falling and moving elements and for collision */
795 #define CHECK_DELAY_MOVING      3
796 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
797 #define CHECK_DELAY_COLLISION   2
798 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
799
800 /* values for initial player move delay (initial delay counter value) */
801 #define INITIAL_MOVE_DELAY_OFF  -1
802 #define INITIAL_MOVE_DELAY_ON   0
803
804 /* values for player movement speed (which is in fact a delay value) */
805 #define MOVE_DELAY_MIN_SPEED    32
806 #define MOVE_DELAY_NORMAL_SPEED 8
807 #define MOVE_DELAY_HIGH_SPEED   4
808 #define MOVE_DELAY_MAX_SPEED    1
809
810 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
811 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
812
813 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
814 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
815
816 /* values for other actions */
817 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
818 #define MOVE_STEPSIZE_MIN       (1)
819 #define MOVE_STEPSIZE_MAX       (TILEX)
820
821 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
822 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
823
824 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
825
826 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
827                                  RND(element_info[e].push_delay_random))
828 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
829                                  RND(element_info[e].drop_delay_random))
830 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
831                                  RND(element_info[e].move_delay_random))
832 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
833                                     (element_info[e].move_delay_random))
834 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
835                                  RND(element_info[e].ce_value_random_initial))
836 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
837 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
838                                  RND((c)->delay_random * (c)->delay_frames))
839 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
840                                  RND((c)->delay_random))
841
842
843 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
844          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
845
846 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
847         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
848          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
849          (be) + (e) - EL_SELF)
850
851 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
852         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
853          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
854          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
855          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
856          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
857          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
858          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
859          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
860          (e))
861
862 #define CAN_GROW_INTO(e)                                                \
863         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
864
865 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
866                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
867                                         (condition)))
868
869 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
870                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
871                                         (CAN_MOVE_INTO_ACID(e) &&       \
872                                          Feld[x][y] == EL_ACID) ||      \
873                                         (condition)))
874
875 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
876                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
877                                         (CAN_MOVE_INTO_ACID(e) &&       \
878                                          Feld[x][y] == EL_ACID) ||      \
879                                         (condition)))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition) ||                  \
884                                         (CAN_MOVE_INTO_ACID(e) &&       \
885                                          Feld[x][y] == EL_ACID) ||      \
886                                         (DONT_COLLIDE_WITH(e) &&        \
887                                          IS_PLAYER(x, y) &&             \
888                                          !PLAYER_ENEMY_PROTECTED(x, y))))
889
890 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
891         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
892
893 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
894         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
895
896 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
897         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
898
899 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
900         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
901                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
902
903 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
904         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
905
906 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
907         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
908
909 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
910         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
911
912 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
913         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
914
915 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
916         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
917
918 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
920                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
921                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
922                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
923                                                  IS_FOOD_PENGUIN(Feld[x][y])))
924 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
925         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
926
927 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
928         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
929
930 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
931         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
932
933 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
934         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
935                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
936
937 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
938
939 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
940                 (!IS_PLAYER(x, y) &&                                    \
941                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
942
943 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
944         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
945
946 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
947 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
948
949 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
950 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
951 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
952 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
953
954 /* game button identifiers */
955 #define GAME_CTRL_ID_STOP               0
956 #define GAME_CTRL_ID_PAUSE              1
957 #define GAME_CTRL_ID_PLAY               2
958 #define SOUND_CTRL_ID_MUSIC             3
959 #define SOUND_CTRL_ID_LOOPS             4
960 #define SOUND_CTRL_ID_SIMPLE            5
961
962 #define NUM_GAME_BUTTONS                6
963
964
965 /* forward declaration for internal use */
966
967 static void CreateField(int, int, int);
968
969 static void ResetGfxAnimation(int, int);
970
971 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
972 static void AdvanceFrameAndPlayerCounters(int);
973
974 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
975 static boolean MovePlayer(struct PlayerInfo *, int, int);
976 static void ScrollPlayer(struct PlayerInfo *, int);
977 static void ScrollScreen(struct PlayerInfo *, int);
978
979 int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
980
981 static void InitBeltMovement(void);
982 static void CloseAllOpenTimegates(void);
983 static void CheckGravityMovement(struct PlayerInfo *);
984 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
985 static void KillPlayerUnlessEnemyProtected(int, int);
986 static void KillPlayerUnlessExplosionProtected(int, int);
987
988 static void TestIfPlayerTouchesCustomElement(int, int);
989 static void TestIfElementTouchesCustomElement(int, int);
990 static void TestIfElementHitsCustomElement(int, int, int);
991 #if 0
992 static void TestIfElementSmashesCustomElement(int, int, int);
993 #endif
994
995 static void HandleElementChange(int, int, int);
996 static void ExecuteCustomElementAction(int, int, int, int);
997 static boolean ChangeElement(int, int, int, int);
998
999 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1000 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1001         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1002 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1003         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1004 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1005         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1006 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1007         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1008
1009 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1010 #define CheckElementChange(x, y, e, te, ev)                             \
1011         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1012 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1013         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1014 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1015         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1016
1017 static void PlayLevelSound(int, int, int);
1018 static void PlayLevelSoundNearest(int, int, int);
1019 static void PlayLevelSoundAction(int, int, int);
1020 static void PlayLevelSoundElementAction(int, int, int, int);
1021 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1022 static void PlayLevelSoundActionIfLoop(int, int, int);
1023 static void StopLevelSoundActionIfLoop(int, int, int);
1024 static void PlayLevelMusic();
1025
1026 static void MapGameButtons();
1027 static void HandleGameButtons(struct GadgetInfo *);
1028
1029 int AmoebeNachbarNr(int, int);
1030 void AmoebeUmwandeln(int, int);
1031 void ContinueMoving(int, int);
1032 void Bang(int, int);
1033 void InitMovDir(int, int);
1034 void InitAmoebaNr(int, int);
1035 int NewHiScore(void);
1036
1037 void TestIfGoodThingHitsBadThing(int, int, int);
1038 void TestIfBadThingHitsGoodThing(int, int, int);
1039 void TestIfPlayerTouchesBadThing(int, int);
1040 void TestIfPlayerRunsIntoBadThing(int, int, int);
1041 void TestIfBadThingTouchesPlayer(int, int);
1042 void TestIfBadThingRunsIntoPlayer(int, int, int);
1043 void TestIfFriendTouchesBadThing(int, int);
1044 void TestIfBadThingTouchesFriend(int, int);
1045 void TestIfBadThingTouchesOtherBadThing(int, int);
1046
1047 void KillPlayer(struct PlayerInfo *);
1048 void BuryPlayer(struct PlayerInfo *);
1049 void RemovePlayer(struct PlayerInfo *);
1050
1051 boolean SnapField(struct PlayerInfo *, int, int);
1052 boolean DropElement(struct PlayerInfo *);
1053
1054 static int getInvisibleActiveFromInvisibleElement(int);
1055 static int getInvisibleFromInvisibleActiveElement(int);
1056
1057 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1058
1059 /* for detection of endless loops, caused by custom element programming */
1060 /* (using maximal playfield width x 10 is just a rough approximation) */
1061 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1062
1063 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1064 {                                                                       \
1065   if (recursion_loop_detected)                                          \
1066     return (rc);                                                        \
1067                                                                         \
1068   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1069   {                                                                     \
1070     recursion_loop_detected = TRUE;                                     \
1071     recursion_loop_element = (e);                                       \
1072   }                                                                     \
1073                                                                         \
1074   recursion_loop_depth++;                                               \
1075 }
1076
1077 #define RECURSION_LOOP_DETECTION_END()                                  \
1078 {                                                                       \
1079   recursion_loop_depth--;                                               \
1080 }
1081
1082 static int recursion_loop_depth;
1083 static boolean recursion_loop_detected;
1084 static boolean recursion_loop_element;
1085
1086
1087 /* ------------------------------------------------------------------------- */
1088 /* definition of elements that automatically change to other elements after  */
1089 /* a specified time, eventually calling a function when changing             */
1090 /* ------------------------------------------------------------------------- */
1091
1092 /* forward declaration for changer functions */
1093 static void InitBuggyBase(int, int);
1094 static void WarnBuggyBase(int, int);
1095
1096 static void InitTrap(int, int);
1097 static void ActivateTrap(int, int);
1098 static void ChangeActiveTrap(int, int);
1099
1100 static void InitRobotWheel(int, int);
1101 static void RunRobotWheel(int, int);
1102 static void StopRobotWheel(int, int);
1103
1104 static void InitTimegateWheel(int, int);
1105 static void RunTimegateWheel(int, int);
1106
1107 static void InitMagicBallDelay(int, int);
1108 static void ActivateMagicBall(int, int);
1109
1110 struct ChangingElementInfo
1111 {
1112   int element;
1113   int target_element;
1114   int change_delay;
1115   void (*pre_change_function)(int x, int y);
1116   void (*change_function)(int x, int y);
1117   void (*post_change_function)(int x, int y);
1118 };
1119
1120 static struct ChangingElementInfo change_delay_list[] =
1121 {
1122   {
1123     EL_NUT_BREAKING,
1124     EL_EMERALD,
1125     6,
1126     NULL,
1127     NULL,
1128     NULL
1129   },
1130   {
1131     EL_PEARL_BREAKING,
1132     EL_EMPTY,
1133     8,
1134     NULL,
1135     NULL,
1136     NULL
1137   },
1138   {
1139     EL_EXIT_OPENING,
1140     EL_EXIT_OPEN,
1141     29,
1142     NULL,
1143     NULL,
1144     NULL
1145   },
1146   {
1147     EL_EXIT_CLOSING,
1148     EL_EXIT_CLOSED,
1149     29,
1150     NULL,
1151     NULL,
1152     NULL
1153   },
1154   {
1155     EL_STEEL_EXIT_OPENING,
1156     EL_STEEL_EXIT_OPEN,
1157     29,
1158     NULL,
1159     NULL,
1160     NULL
1161   },
1162   {
1163     EL_STEEL_EXIT_CLOSING,
1164     EL_STEEL_EXIT_CLOSED,
1165     29,
1166     NULL,
1167     NULL,
1168     NULL
1169   },
1170   {
1171     EL_EM_EXIT_OPENING,
1172     EL_EM_EXIT_OPEN,
1173     29,
1174     NULL,
1175     NULL,
1176     NULL
1177   },
1178   {
1179     EL_EM_EXIT_CLOSING,
1180 #if 1
1181     EL_EMPTY,
1182 #else
1183     EL_EM_EXIT_CLOSED,
1184 #endif
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_STEEL_EXIT_OPENING,
1192     EL_EM_STEEL_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_STEEL_EXIT_CLOSING,
1200 #if 1
1201     EL_STEELWALL,
1202 #else
1203     EL_EM_STEEL_EXIT_CLOSED,
1204 #endif
1205     29,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_SP_EXIT_OPENING,
1212     EL_SP_EXIT_OPEN,
1213     29,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_SP_EXIT_CLOSING,
1220     EL_SP_EXIT_CLOSED,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_SWITCHGATE_OPENING,
1228     EL_SWITCHGATE_OPEN,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_SWITCHGATE_CLOSING,
1236     EL_SWITCHGATE_CLOSED,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_TIMEGATE_OPENING,
1244     EL_TIMEGATE_OPEN,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_TIMEGATE_CLOSING,
1252     EL_TIMEGATE_CLOSED,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258
1259   {
1260     EL_ACID_SPLASH_LEFT,
1261     EL_EMPTY,
1262     8,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_ACID_SPLASH_RIGHT,
1269     EL_EMPTY,
1270     8,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_BUGGY_BASE,
1277     EL_SP_BUGGY_BASE_ACTIVATING,
1278     0,
1279     InitBuggyBase,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SP_BUGGY_BASE_ACTIVATING,
1285     EL_SP_BUGGY_BASE_ACTIVE,
1286     0,
1287     InitBuggyBase,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SP_BUGGY_BASE_ACTIVE,
1293     EL_SP_BUGGY_BASE,
1294     0,
1295     InitBuggyBase,
1296     WarnBuggyBase,
1297     NULL
1298   },
1299   {
1300     EL_TRAP,
1301     EL_TRAP_ACTIVE,
1302     0,
1303     InitTrap,
1304     NULL,
1305     ActivateTrap
1306   },
1307   {
1308     EL_TRAP_ACTIVE,
1309     EL_TRAP,
1310     31,
1311     NULL,
1312     ChangeActiveTrap,
1313     NULL
1314   },
1315   {
1316     EL_ROBOT_WHEEL_ACTIVE,
1317     EL_ROBOT_WHEEL,
1318     0,
1319     InitRobotWheel,
1320     RunRobotWheel,
1321     StopRobotWheel
1322   },
1323   {
1324     EL_TIMEGATE_SWITCH_ACTIVE,
1325     EL_TIMEGATE_SWITCH,
1326     0,
1327     InitTimegateWheel,
1328     RunTimegateWheel,
1329     NULL
1330   },
1331   {
1332     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1333     EL_DC_TIMEGATE_SWITCH,
1334     0,
1335     InitTimegateWheel,
1336     RunTimegateWheel,
1337     NULL
1338   },
1339   {
1340     EL_EMC_MAGIC_BALL_ACTIVE,
1341     EL_EMC_MAGIC_BALL_ACTIVE,
1342     0,
1343     InitMagicBallDelay,
1344     NULL,
1345     ActivateMagicBall
1346   },
1347   {
1348     EL_EMC_SPRING_BUMPER_ACTIVE,
1349     EL_EMC_SPRING_BUMPER,
1350     8,
1351     NULL,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_DIAGONAL_SHRINKING,
1357     EL_UNDEFINED,
1358     0,
1359     NULL,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_DIAGONAL_GROWING,
1365     EL_UNDEFINED,
1366     0,
1367     NULL,
1368     NULL,
1369     NULL,
1370   },
1371
1372   {
1373     EL_UNDEFINED,
1374     EL_UNDEFINED,
1375     -1,
1376     NULL,
1377     NULL,
1378     NULL
1379   }
1380 };
1381
1382 struct
1383 {
1384   int element;
1385   int push_delay_fixed, push_delay_random;
1386 }
1387 push_delay_list[] =
1388 {
1389   { EL_SPRING,                  0, 0 },
1390   { EL_BALLOON,                 0, 0 },
1391
1392   { EL_SOKOBAN_OBJECT,          2, 0 },
1393   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1394   { EL_SATELLITE,               2, 0 },
1395   { EL_SP_DISK_YELLOW,          2, 0 },
1396
1397   { EL_UNDEFINED,               0, 0 },
1398 };
1399
1400 struct
1401 {
1402   int element;
1403   int move_stepsize;
1404 }
1405 move_stepsize_list[] =
1406 {
1407   { EL_AMOEBA_DROP,             2 },
1408   { EL_AMOEBA_DROPPING,         2 },
1409   { EL_QUICKSAND_FILLING,       1 },
1410   { EL_QUICKSAND_EMPTYING,      1 },
1411   { EL_QUICKSAND_FAST_FILLING,  2 },
1412   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1413   { EL_MAGIC_WALL_FILLING,      2 },
1414   { EL_MAGIC_WALL_EMPTYING,     2 },
1415   { EL_BD_MAGIC_WALL_FILLING,   2 },
1416   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1417   { EL_DC_MAGIC_WALL_FILLING,   2 },
1418   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1419
1420   { EL_UNDEFINED,               0 },
1421 };
1422
1423 struct
1424 {
1425   int element;
1426   int count;
1427 }
1428 collect_count_list[] =
1429 {
1430   { EL_EMERALD,                 1 },
1431   { EL_BD_DIAMOND,              1 },
1432   { EL_EMERALD_YELLOW,          1 },
1433   { EL_EMERALD_RED,             1 },
1434   { EL_EMERALD_PURPLE,          1 },
1435   { EL_DIAMOND,                 3 },
1436   { EL_SP_INFOTRON,             1 },
1437   { EL_PEARL,                   5 },
1438   { EL_CRYSTAL,                 8 },
1439
1440   { EL_UNDEFINED,               0 },
1441 };
1442
1443 struct
1444 {
1445   int element;
1446   int direction;
1447 }
1448 access_direction_list[] =
1449 {
1450   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1451   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1452   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1453   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1454   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1455   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1456   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1457   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1458   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1459   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1460   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1461
1462   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1463   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1464   { EL_SP_PORT_UP,                                                   MV_DOWN },
1465   { EL_SP_PORT_DOWN,                                         MV_UP           },
1466   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1467   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1468   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1469   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1470   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1471   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1472   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1473   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1474   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1475   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1476   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1477   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1478   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1479   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1480   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1481
1482   { EL_UNDEFINED,                       MV_NONE                              }
1483 };
1484
1485 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1486
1487 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1488 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1489 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1490                                  IS_JUST_CHANGING(x, y))
1491
1492 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1493
1494 /* static variables for playfield scan mode (scanning forward or backward) */
1495 static int playfield_scan_start_x = 0;
1496 static int playfield_scan_start_y = 0;
1497 static int playfield_scan_delta_x = 1;
1498 static int playfield_scan_delta_y = 1;
1499
1500 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1501                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1502                                      (y) += playfield_scan_delta_y)     \
1503                                 for ((x) = playfield_scan_start_x;      \
1504                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1505                                      (x) += playfield_scan_delta_x)
1506
1507 #ifdef DEBUG
1508 void DEBUG_SetMaximumDynamite()
1509 {
1510   int i;
1511
1512   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1513     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1514       local_player->inventory_element[local_player->inventory_size++] =
1515         EL_DYNAMITE;
1516 }
1517 #endif
1518
1519 static void InitPlayfieldScanModeVars()
1520 {
1521   if (game.use_reverse_scan_direction)
1522   {
1523     playfield_scan_start_x = lev_fieldx - 1;
1524     playfield_scan_start_y = lev_fieldy - 1;
1525
1526     playfield_scan_delta_x = -1;
1527     playfield_scan_delta_y = -1;
1528   }
1529   else
1530   {
1531     playfield_scan_start_x = 0;
1532     playfield_scan_start_y = 0;
1533
1534     playfield_scan_delta_x = 1;
1535     playfield_scan_delta_y = 1;
1536   }
1537 }
1538
1539 static void InitPlayfieldScanMode(int mode)
1540 {
1541   game.use_reverse_scan_direction =
1542     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1543
1544   InitPlayfieldScanModeVars();
1545 }
1546
1547 static int get_move_delay_from_stepsize(int move_stepsize)
1548 {
1549   move_stepsize =
1550     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1551
1552   /* make sure that stepsize value is always a power of 2 */
1553   move_stepsize = (1 << log_2(move_stepsize));
1554
1555   return TILEX / move_stepsize;
1556 }
1557
1558 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1559                                boolean init_game)
1560 {
1561   int player_nr = player->index_nr;
1562   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1563   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1564
1565   /* do no immediately change move delay -- the player might just be moving */
1566   player->move_delay_value_next = move_delay;
1567
1568   /* information if player can move must be set separately */
1569   player->cannot_move = cannot_move;
1570
1571   if (init_game)
1572   {
1573     player->move_delay       = game.initial_move_delay[player_nr];
1574     player->move_delay_value = game.initial_move_delay_value[player_nr];
1575
1576     player->move_delay_value_next = -1;
1577
1578     player->move_delay_reset_counter = 0;
1579   }
1580 }
1581
1582 void GetPlayerConfig()
1583 {
1584   GameFrameDelay = setup.game_frame_delay;
1585
1586   if (!audio.sound_available)
1587     setup.sound_simple = FALSE;
1588
1589   if (!audio.loops_available)
1590     setup.sound_loops = FALSE;
1591
1592   if (!audio.music_available)
1593     setup.sound_music = FALSE;
1594
1595   if (!video.fullscreen_available)
1596     setup.fullscreen = FALSE;
1597
1598   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1599
1600   SetAudioMode(setup.sound);
1601   InitJoysticks();
1602 }
1603
1604 int GetElementFromGroupElement(int element)
1605 {
1606   if (IS_GROUP_ELEMENT(element))
1607   {
1608     struct ElementGroupInfo *group = element_info[element].group;
1609     int last_anim_random_frame = gfx.anim_random_frame;
1610     int element_pos;
1611
1612     if (group->choice_mode == ANIM_RANDOM)
1613       gfx.anim_random_frame = RND(group->num_elements_resolved);
1614
1615     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1616                                     group->choice_mode, 0,
1617                                     group->choice_pos);
1618
1619     if (group->choice_mode == ANIM_RANDOM)
1620       gfx.anim_random_frame = last_anim_random_frame;
1621
1622     group->choice_pos++;
1623
1624     element = group->element_resolved[element_pos];
1625   }
1626
1627   return element;
1628 }
1629
1630 static void InitPlayerField(int x, int y, int element, boolean init_game)
1631 {
1632   if (element == EL_SP_MURPHY)
1633   {
1634     if (init_game)
1635     {
1636       if (stored_player[0].present)
1637       {
1638         Feld[x][y] = EL_SP_MURPHY_CLONE;
1639
1640         return;
1641       }
1642       else
1643       {
1644         stored_player[0].use_murphy = TRUE;
1645
1646         if (!level.use_artwork_element[0])
1647           stored_player[0].artwork_element = EL_SP_MURPHY;
1648       }
1649
1650       Feld[x][y] = EL_PLAYER_1;
1651     }
1652   }
1653
1654   if (init_game)
1655   {
1656     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1657     int jx = player->jx, jy = player->jy;
1658
1659     player->present = TRUE;
1660
1661     player->block_last_field = (element == EL_SP_MURPHY ?
1662                                 level.sp_block_last_field :
1663                                 level.block_last_field);
1664
1665     /* ---------- initialize player's last field block delay --------------- */
1666
1667     /* always start with reliable default value (no adjustment needed) */
1668     player->block_delay_adjustment = 0;
1669
1670     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1671     if (player->block_last_field && element == EL_SP_MURPHY)
1672       player->block_delay_adjustment = 1;
1673
1674     /* special case 2: in game engines before 3.1.1, blocking was different */
1675     if (game.use_block_last_field_bug)
1676       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1677
1678     if (!options.network || player->connected)
1679     {
1680       player->active = TRUE;
1681
1682       /* remove potentially duplicate players */
1683       if (StorePlayer[jx][jy] == Feld[x][y])
1684         StorePlayer[jx][jy] = 0;
1685
1686       StorePlayer[x][y] = Feld[x][y];
1687
1688       if (options.debug)
1689       {
1690         printf("Player %d activated.\n", player->element_nr);
1691         printf("[Local player is %d and currently %s.]\n",
1692                local_player->element_nr,
1693                local_player->active ? "active" : "not active");
1694       }
1695     }
1696
1697     Feld[x][y] = EL_EMPTY;
1698
1699     player->jx = player->last_jx = x;
1700     player->jy = player->last_jy = y;
1701   }
1702 }
1703
1704 static void InitField(int x, int y, boolean init_game)
1705 {
1706   int element = Feld[x][y];
1707
1708   switch (element)
1709   {
1710     case EL_SP_MURPHY:
1711     case EL_PLAYER_1:
1712     case EL_PLAYER_2:
1713     case EL_PLAYER_3:
1714     case EL_PLAYER_4:
1715       InitPlayerField(x, y, element, init_game);
1716       break;
1717
1718     case EL_SOKOBAN_FIELD_PLAYER:
1719       element = Feld[x][y] = EL_PLAYER_1;
1720       InitField(x, y, init_game);
1721
1722       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1723       InitField(x, y, init_game);
1724       break;
1725
1726     case EL_SOKOBAN_FIELD_EMPTY:
1727       local_player->sokobanfields_still_needed++;
1728       break;
1729
1730     case EL_STONEBLOCK:
1731       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1732         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1733       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1734         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1735       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1736         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1737       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1738         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1739       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1740         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1741       break;
1742
1743     case EL_BUG:
1744     case EL_BUG_RIGHT:
1745     case EL_BUG_UP:
1746     case EL_BUG_LEFT:
1747     case EL_BUG_DOWN:
1748     case EL_SPACESHIP:
1749     case EL_SPACESHIP_RIGHT:
1750     case EL_SPACESHIP_UP:
1751     case EL_SPACESHIP_LEFT:
1752     case EL_SPACESHIP_DOWN:
1753     case EL_BD_BUTTERFLY:
1754     case EL_BD_BUTTERFLY_RIGHT:
1755     case EL_BD_BUTTERFLY_UP:
1756     case EL_BD_BUTTERFLY_LEFT:
1757     case EL_BD_BUTTERFLY_DOWN:
1758     case EL_BD_FIREFLY:
1759     case EL_BD_FIREFLY_RIGHT:
1760     case EL_BD_FIREFLY_UP:
1761     case EL_BD_FIREFLY_LEFT:
1762     case EL_BD_FIREFLY_DOWN:
1763     case EL_PACMAN_RIGHT:
1764     case EL_PACMAN_UP:
1765     case EL_PACMAN_LEFT:
1766     case EL_PACMAN_DOWN:
1767     case EL_YAMYAM:
1768     case EL_YAMYAM_LEFT:
1769     case EL_YAMYAM_RIGHT:
1770     case EL_YAMYAM_UP:
1771     case EL_YAMYAM_DOWN:
1772     case EL_DARK_YAMYAM:
1773     case EL_ROBOT:
1774     case EL_PACMAN:
1775     case EL_SP_SNIKSNAK:
1776     case EL_SP_ELECTRON:
1777     case EL_MOLE:
1778     case EL_MOLE_LEFT:
1779     case EL_MOLE_RIGHT:
1780     case EL_MOLE_UP:
1781     case EL_MOLE_DOWN:
1782       InitMovDir(x, y);
1783       break;
1784
1785     case EL_AMOEBA_FULL:
1786     case EL_BD_AMOEBA:
1787       InitAmoebaNr(x, y);
1788       break;
1789
1790     case EL_AMOEBA_DROP:
1791       if (y == lev_fieldy - 1)
1792       {
1793         Feld[x][y] = EL_AMOEBA_GROWING;
1794         Store[x][y] = EL_AMOEBA_WET;
1795       }
1796       break;
1797
1798     case EL_DYNAMITE_ACTIVE:
1799     case EL_SP_DISK_RED_ACTIVE:
1800     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1801     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1802     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1803     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1804       MovDelay[x][y] = 96;
1805       break;
1806
1807     case EL_EM_DYNAMITE_ACTIVE:
1808       MovDelay[x][y] = 32;
1809       break;
1810
1811     case EL_LAMP:
1812       local_player->lights_still_needed++;
1813       break;
1814
1815     case EL_PENGUIN:
1816       local_player->friends_still_needed++;
1817       break;
1818
1819     case EL_PIG:
1820     case EL_DRAGON:
1821       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1822       break;
1823
1824     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1825     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1826     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1827     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1828     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1829     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1830     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1831     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1832     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1833     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1834     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1835     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1836       if (init_game)
1837       {
1838         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1839         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1840         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1841
1842         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1843         {
1844           game.belt_dir[belt_nr] = belt_dir;
1845           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1846         }
1847         else    /* more than one switch -- set it like the first switch */
1848         {
1849           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1850         }
1851       }
1852       break;
1853
1854 #if !USE_BOTH_SWITCHGATE_SWITCHES
1855     case EL_SWITCHGATE_SWITCH_DOWN:     /* always start with same switch pos */
1856       if (init_game)
1857         Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1858       break;
1859
1860     case EL_DC_SWITCHGATE_SWITCH_DOWN:  /* always start with same switch pos */
1861       if (init_game)
1862         Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1863       break;
1864 #endif
1865
1866     case EL_LIGHT_SWITCH_ACTIVE:
1867       if (init_game)
1868         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1869       break;
1870
1871     case EL_INVISIBLE_STEELWALL:
1872     case EL_INVISIBLE_WALL:
1873     case EL_INVISIBLE_SAND:
1874       if (game.light_time_left > 0 ||
1875           game.lenses_time_left > 0)
1876         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1877       break;
1878
1879     case EL_EMC_MAGIC_BALL:
1880       if (game.ball_state)
1881         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1882       break;
1883
1884     case EL_EMC_MAGIC_BALL_SWITCH:
1885       if (game.ball_state)
1886         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1887       break;
1888
1889     default:
1890       if (IS_CUSTOM_ELEMENT(element))
1891       {
1892         if (CAN_MOVE(element))
1893           InitMovDir(x, y);
1894
1895 #if USE_NEW_CUSTOM_VALUE
1896         if (!element_info[element].use_last_ce_value || init_game)
1897           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1898 #endif
1899       }
1900       else if (IS_GROUP_ELEMENT(element))
1901       {
1902         Feld[x][y] = GetElementFromGroupElement(element);
1903
1904         InitField(x, y, init_game);
1905       }
1906
1907       break;
1908   }
1909
1910   if (!init_game)
1911     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1912 }
1913
1914 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1915 {
1916   InitField(x, y, init_game);
1917
1918   /* not needed to call InitMovDir() -- already done by InitField()! */
1919   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1920       CAN_MOVE(Feld[x][y]))
1921     InitMovDir(x, y);
1922 }
1923
1924 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1925 {
1926   int old_element = Feld[x][y];
1927
1928   InitField(x, y, init_game);
1929
1930   /* not needed to call InitMovDir() -- already done by InitField()! */
1931   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1932       CAN_MOVE(old_element) &&
1933       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1934     InitMovDir(x, y);
1935
1936   /* this case is in fact a combination of not less than three bugs:
1937      first, it calls InitMovDir() for elements that can move, although this is
1938      already done by InitField(); then, it checks the element that was at this
1939      field _before_ the call to InitField() (which can change it); lastly, it
1940      was not called for "mole with direction" elements, which were treated as
1941      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1942   */
1943 }
1944
1945 #if 1
1946
1947 static int get_key_element_from_nr(int key_nr)
1948 {
1949   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1950                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1951                           EL_EM_KEY_1 : EL_KEY_1);
1952
1953   return key_base_element + key_nr;
1954 }
1955
1956 static int get_next_dropped_element(struct PlayerInfo *player)
1957 {
1958   return (player->inventory_size > 0 ?
1959           player->inventory_element[player->inventory_size - 1] :
1960           player->inventory_infinite_element != EL_UNDEFINED ?
1961           player->inventory_infinite_element :
1962           player->dynabombs_left > 0 ?
1963           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1964           EL_UNDEFINED);
1965 }
1966
1967 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
1968 {
1969   /* pos >= 0: get element from bottom of the stack;
1970      pos <  0: get element from top of the stack */
1971
1972   if (pos < 0)
1973   {
1974     int min_inventory_size = -pos;
1975     int inventory_pos = player->inventory_size - min_inventory_size;
1976     int min_dynabombs_left = min_inventory_size - player->inventory_size;
1977
1978     return (player->inventory_size >= min_inventory_size ?
1979             player->inventory_element[inventory_pos] :
1980             player->inventory_infinite_element != EL_UNDEFINED ?
1981             player->inventory_infinite_element :
1982             player->dynabombs_left >= min_dynabombs_left ?
1983             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1984             EL_UNDEFINED);
1985   }
1986   else
1987   {
1988     int min_dynabombs_left = pos + 1;
1989     int min_inventory_size = pos + 1 - player->dynabombs_left;
1990     int inventory_pos = pos - player->dynabombs_left;
1991
1992     return (player->inventory_infinite_element != EL_UNDEFINED ?
1993             player->inventory_infinite_element :
1994             player->dynabombs_left >= min_dynabombs_left ?
1995             EL_DYNABOMB_PLAYER_1 + player->index_nr :
1996             player->inventory_size >= min_inventory_size ?
1997             player->inventory_element[inventory_pos] :
1998             EL_UNDEFINED);
1999   }
2000 }
2001
2002 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2003 {
2004   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2005   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2006   int compare_result;
2007
2008   if (gpo1->sort_priority != gpo2->sort_priority)
2009     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2010   else
2011     compare_result = gpo1->nr - gpo2->nr;
2012
2013   return compare_result;
2014 }
2015
2016 void InitGameControlValues()
2017 {
2018   int i;
2019
2020   for (i = 0; game_panel_controls[i].nr != -1; i++)
2021   {
2022     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2023     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2024     struct TextPosInfo *pos = gpc->pos;
2025     int nr = gpc->nr;
2026     int type = gpc->type;
2027
2028     if (nr != i)
2029     {
2030       Error(ERR_INFO, "'game_panel_controls' structure corrupted");
2031       Error(ERR_EXIT, "this should not happen -- please debug");
2032     }
2033
2034     /* force update of game controls after initialization */
2035     gpc->value = gpc->last_value = -1;
2036     gpc->frame = gpc->last_frame = -1;
2037     gpc->gfx_frame = -1;
2038
2039     /* determine panel value width for later calculation of alignment */
2040     if (type == TYPE_INTEGER || type == TYPE_STRING)
2041     {
2042       pos->width = pos->size * getFontWidth(pos->font);
2043       pos->height = getFontHeight(pos->font);
2044     }
2045     else if (type == TYPE_ELEMENT)
2046     {
2047       pos->width = pos->size;
2048       pos->height = pos->size;
2049     }
2050
2051     /* fill structure for game panel draw order */
2052     gpo->nr = gpc->nr;
2053     gpo->sort_priority = pos->sort_priority;
2054   }
2055
2056   /* sort game panel controls according to sort_priority and control number */
2057   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2058         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2059 }
2060
2061 void UpdatePlayfieldElementCount()
2062 {
2063   int i, j, x, y;
2064
2065   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2066     element_info[i].element_count = 0;
2067
2068   SCAN_PLAYFIELD(x, y)
2069   {
2070     element_info[Feld[x][y]].element_count++;
2071   }
2072
2073   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2074     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2075       if (IS_IN_GROUP(j, i))
2076         element_info[EL_GROUP_START + i].element_count +=
2077           element_info[j].element_count;
2078 }
2079
2080 void UpdateGameControlValues()
2081 {
2082   int i, k;
2083   int time = (local_player->LevelSolved ?
2084               local_player->LevelSolved_CountingTime :
2085               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2086               level.native_em_level->lev->time :
2087               level.time == 0 ? TimePlayed : TimeLeft);
2088   int score = (local_player->LevelSolved ?
2089                local_player->LevelSolved_CountingScore :
2090                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2091                level.native_em_level->lev->score :
2092                local_player->score);
2093   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2094               level.native_em_level->lev->required :
2095               local_player->gems_still_needed);
2096   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2097                      level.native_em_level->lev->required > 0 :
2098                      local_player->gems_still_needed > 0 ||
2099                      local_player->sokobanfields_still_needed > 0 ||
2100                      local_player->lights_still_needed > 0);
2101
2102   UpdatePlayfieldElementCount();
2103
2104   /* update game panel control values */
2105
2106   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2107   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2108
2109   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2110   for (i = 0; i < MAX_NUM_KEYS; i++)
2111     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2112   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2113   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2114
2115   if (game.centered_player_nr == -1)
2116   {
2117     for (i = 0; i < MAX_PLAYERS; i++)
2118     {
2119       for (k = 0; k < MAX_NUM_KEYS; k++)
2120       {
2121         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2122         {
2123           if (level.native_em_level->ply[i]->keys & (1 << k))
2124             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2125               get_key_element_from_nr(k);
2126         }
2127         else if (stored_player[i].key[k])
2128           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2129             get_key_element_from_nr(k);
2130       }
2131
2132       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2133         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2134           level.native_em_level->ply[i]->dynamite;
2135       else
2136         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2137           stored_player[i].inventory_size;
2138
2139       if (stored_player[i].num_white_keys > 0)
2140         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2141           EL_DC_KEY_WHITE;
2142
2143       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2144         stored_player[i].num_white_keys;
2145     }
2146   }
2147   else
2148   {
2149     int player_nr = game.centered_player_nr;
2150
2151     for (k = 0; k < MAX_NUM_KEYS; k++)
2152     {
2153       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2154       {
2155         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2156           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2157             get_key_element_from_nr(k);
2158       }
2159       else if (stored_player[player_nr].key[k])
2160         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2161           get_key_element_from_nr(k);
2162     }
2163
2164     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2165       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2166         level.native_em_level->ply[player_nr]->dynamite;
2167     else
2168       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2169         stored_player[player_nr].inventory_size;
2170
2171     if (stored_player[player_nr].num_white_keys > 0)
2172       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2173
2174     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2175       stored_player[player_nr].num_white_keys;
2176   }
2177
2178   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2179   {
2180     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2181       get_inventory_element_from_pos(local_player, i);
2182     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2183       get_inventory_element_from_pos(local_player, -i - 1);
2184   }
2185
2186   game_panel_controls[GAME_PANEL_SCORE].value = score;
2187
2188   game_panel_controls[GAME_PANEL_TIME].value = time;
2189
2190   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2191   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2192   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2193
2194   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2195     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2196      EL_EMPTY);
2197   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2198     local_player->shield_normal_time_left;
2199   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2200     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2201      EL_EMPTY);
2202   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2203     local_player->shield_deadly_time_left;
2204
2205   game_panel_controls[GAME_PANEL_EXIT].value =
2206     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2207
2208   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2209     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2210   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2211     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2212      EL_EMC_MAGIC_BALL_SWITCH);
2213
2214   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2215     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2216   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2217     game.light_time_left;
2218
2219   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2220     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2221   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2222     game.timegate_time_left;
2223
2224   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2225     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2226
2227   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2228     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2229   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2230     game.lenses_time_left;
2231
2232   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2233     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2234   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2235     game.magnify_time_left;
2236
2237   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2238     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2239      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2240      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2241      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2242      EL_BALLOON_SWITCH_NONE);
2243
2244   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2245     local_player->dynabomb_count;
2246   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2247     local_player->dynabomb_size;
2248   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2249     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2250
2251   game_panel_controls[GAME_PANEL_PENGUINS].value =
2252     local_player->friends_still_needed;
2253
2254   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2255     local_player->sokobanfields_still_needed;
2256   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2257     local_player->sokobanfields_still_needed;
2258
2259   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2260     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2261
2262   for (i = 0; i < NUM_BELTS; i++)
2263   {
2264     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2265       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2266        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2267     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2268       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2269   }
2270
2271   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2272     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2273   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2274     game.magic_wall_time_left;
2275
2276 #if USE_PLAYER_GRAVITY
2277   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2278     local_player->gravity;
2279 #else
2280   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2281 #endif
2282
2283   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2284     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2285       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2286        game.panel.element[i].id : EL_UNDEFINED);
2287
2288   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2289     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2290       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2291        element_info[game.panel.element_count[i].id].element_count :
2292        EL_UNDEFINED);
2293
2294   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2295     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2296       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2297        element_info[game.panel.ce_score[i].id].collect_score : 0);
2298
2299   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2300     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2301       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2302        element_info[game.panel.ce_score_element[i].id].collect_score :
2303        EL_UNDEFINED);
2304
2305   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2306   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2307   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2308
2309   /* update game panel control frames */
2310
2311   for (i = 0; game_panel_controls[i].nr != -1; i++)
2312   {
2313     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2314
2315     if (gpc->type == TYPE_ELEMENT)
2316     {
2317       int last_anim_random_frame = gfx.anim_random_frame;
2318       int element = gpc->value;
2319       int graphic = el2panelimg(element);
2320
2321       if (gpc->value != gpc->last_value)
2322       {
2323         gpc->gfx_frame = 0;
2324         gpc->gfx_random = INIT_GFX_RANDOM();
2325       }
2326       else
2327       {
2328         gpc->gfx_frame++;
2329
2330         if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2331             IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2332           gpc->gfx_random = INIT_GFX_RANDOM();
2333       }
2334
2335       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2336         gfx.anim_random_frame = gpc->gfx_random;
2337
2338       if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2339         gpc->gfx_frame = element_info[element].collect_score;
2340
2341       gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2342                                             gpc->gfx_frame);
2343
2344       if (ANIM_MODE(graphic) == ANIM_RANDOM)
2345         gfx.anim_random_frame = last_anim_random_frame;
2346     }
2347   }
2348 }
2349
2350 void DisplayGameControlValues()
2351 {
2352   boolean redraw_panel = FALSE;
2353   int i;
2354
2355   for (i = 0; game_panel_controls[i].nr != -1; i++)
2356   {
2357     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2358
2359     if (PANEL_DEACTIVATED(gpc->pos))
2360       continue;
2361
2362     if (gpc->value == gpc->last_value &&
2363         gpc->frame == gpc->last_frame)
2364       continue;
2365
2366     redraw_panel = TRUE;
2367   }
2368
2369   if (!redraw_panel)
2370     return;
2371
2372   /* copy default game door content to main double buffer */
2373   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2374              DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2375
2376   /* redraw game control buttons */
2377 #if 1
2378   RedrawGameButtons();
2379 #else
2380   UnmapGameButtons();
2381   MapGameButtons();
2382 #endif
2383
2384   game_status = GAME_MODE_PSEUDO_PANEL;
2385
2386 #if 1
2387   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2388 #else
2389   for (i = 0; game_panel_controls[i].nr != -1; i++)
2390 #endif
2391   {
2392 #if 1
2393     int nr = game_panel_order[i].nr;
2394     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2395 #else
2396     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2397     int nr = gpc->nr;
2398 #endif
2399     struct TextPosInfo *pos = gpc->pos;
2400     int type = gpc->type;
2401     int value = gpc->value;
2402     int frame = gpc->frame;
2403 #if 0
2404     int last_value = gpc->last_value;
2405     int last_frame = gpc->last_frame;
2406 #endif
2407     int size = pos->size;
2408     int font = pos->font;
2409     boolean draw_masked = pos->draw_masked;
2410     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2411
2412     if (PANEL_DEACTIVATED(pos))
2413       continue;
2414
2415 #if 0
2416     if (value == last_value && frame == last_frame)
2417       continue;
2418 #endif
2419
2420     gpc->last_value = value;
2421     gpc->last_frame = frame;
2422
2423 #if 0
2424     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2425 #endif
2426
2427     if (type == TYPE_INTEGER)
2428     {
2429       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2430           nr == GAME_PANEL_TIME)
2431       {
2432         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2433
2434         if (use_dynamic_size)           /* use dynamic number of digits */
2435         {
2436           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2437           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2438           int size2 = size1 + 1;
2439           int font1 = pos->font;
2440           int font2 = pos->font_alt;
2441
2442           size = (value < value_change ? size1 : size2);
2443           font = (value < value_change ? font1 : font2);
2444
2445 #if 0
2446           /* clear background if value just changed its size (dynamic digits) */
2447           if ((last_value < value_change) != (value < value_change))
2448           {
2449             int width1 = size1 * getFontWidth(font1);
2450             int width2 = size2 * getFontWidth(font2);
2451             int max_width = MAX(width1, width2);
2452             int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2453
2454             pos->width = max_width;
2455
2456             ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2457                                        max_width, max_height);
2458           }
2459 #endif
2460         }
2461       }
2462
2463 #if 1
2464       /* correct text size if "digits" is zero or less */
2465       if (size <= 0)
2466         size = strlen(int2str(value, size));
2467
2468       /* dynamically correct text alignment */
2469       pos->width = size * getFontWidth(font);
2470 #endif
2471
2472       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2473                   int2str(value, size), font, mask_mode);
2474     }
2475     else if (type == TYPE_ELEMENT)
2476     {
2477       int element, graphic;
2478       Bitmap *src_bitmap;
2479       int src_x, src_y;
2480       int width, height;
2481       int dst_x = PANEL_XPOS(pos);
2482       int dst_y = PANEL_YPOS(pos);
2483
2484 #if 1
2485       if (value != EL_UNDEFINED && value != EL_EMPTY)
2486       {
2487         element = value;
2488         graphic = el2panelimg(value);
2489
2490         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2491                               &src_x, &src_y);
2492
2493         width  = graphic_info[graphic].width  * size / TILESIZE;
2494         height = graphic_info[graphic].height * size / TILESIZE;
2495
2496         if (draw_masked)
2497         {
2498           SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2499                         dst_x - src_x, dst_y - src_y);
2500           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2501                            dst_x, dst_y);
2502         }
2503         else
2504         {
2505           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2506                      dst_x, dst_y);
2507         }
2508       }
2509 #else
2510       if (value == EL_UNDEFINED || value == EL_EMPTY)
2511       {
2512         element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2513         graphic = el2panelimg(element);
2514
2515         src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2516         src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2517         src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2518       }
2519       else
2520       {
2521         element = value;
2522         graphic = el2panelimg(value);
2523
2524         getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2525       }
2526
2527       width  = graphic_info[graphic].width  * size / TILESIZE;
2528       height = graphic_info[graphic].height * size / TILESIZE;
2529
2530       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2531 #endif
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548 #if 0
2549         int size1 = strlen(state_normal);
2550         int size2 = strlen(state_active);
2551         int width1 = size1 * getFontWidth(font1);
2552         int width2 = size2 * getFontWidth(font2);
2553         int max_width = MAX(width1, width2);
2554         int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2555
2556         pos->width = max_width;
2557
2558         /* clear background for values that may have changed its size */
2559         ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2560                                    max_width, max_height);
2561 #endif
2562
2563         font = (active ? font2 : font1);
2564       }
2565
2566       if (s != NULL)
2567       {
2568         char *s_cut;
2569
2570 #if 1
2571         if (size <= 0)
2572         {
2573           /* don't truncate output if "chars" is zero or less */
2574           size = strlen(s);
2575
2576           /* dynamically correct text alignment */
2577           pos->width = size * getFontWidth(font);
2578         }
2579 #endif
2580
2581         s_cut = getStringCopyN(s, size);
2582
2583         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2584                     s_cut, font, mask_mode);
2585
2586         free(s_cut);
2587       }
2588     }
2589
2590     redraw_mask |= REDRAW_DOOR_1;
2591   }
2592
2593   game_status = GAME_MODE_PLAYING;
2594 }
2595
2596 void DrawGameValue_Emeralds(int value)
2597 {
2598   struct TextPosInfo *pos = &game.panel.gems;
2599 #if 1
2600   int font_nr = pos->font;
2601 #else
2602   int font_nr = FONT_TEXT_2;
2603 #endif
2604   int font_width = getFontWidth(font_nr);
2605   int chars = pos->size;
2606
2607 #if 1
2608   return;       /* !!! USE NEW STUFF !!! */
2609 #endif
2610
2611   if (PANEL_DEACTIVATED(pos))
2612     return;
2613
2614   pos->width = chars * font_width;
2615
2616   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2617 }
2618
2619 void DrawGameValue_Dynamite(int value)
2620 {
2621   struct TextPosInfo *pos = &game.panel.inventory_count;
2622 #if 1
2623   int font_nr = pos->font;
2624 #else
2625   int font_nr = FONT_TEXT_2;
2626 #endif
2627   int font_width = getFontWidth(font_nr);
2628   int chars = pos->size;
2629
2630 #if 1
2631   return;       /* !!! USE NEW STUFF !!! */
2632 #endif
2633
2634   if (PANEL_DEACTIVATED(pos))
2635     return;
2636
2637   pos->width = chars * font_width;
2638
2639   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2640 }
2641
2642 void DrawGameValue_Score(int value)
2643 {
2644   struct TextPosInfo *pos = &game.panel.score;
2645 #if 1
2646   int font_nr = pos->font;
2647 #else
2648   int font_nr = FONT_TEXT_2;
2649 #endif
2650   int font_width = getFontWidth(font_nr);
2651   int chars = pos->size;
2652
2653 #if 1
2654   return;       /* !!! USE NEW STUFF !!! */
2655 #endif
2656
2657   if (PANEL_DEACTIVATED(pos))
2658     return;
2659
2660   pos->width = chars * font_width;
2661
2662   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2663 }
2664
2665 void DrawGameValue_Time(int value)
2666 {
2667   struct TextPosInfo *pos = &game.panel.time;
2668   static int last_value = -1;
2669   int chars1 = 3;
2670   int chars2 = 4;
2671   int chars = pos->size;
2672 #if 1
2673   int font1_nr = pos->font;
2674   int font2_nr = pos->font_alt;
2675 #else
2676   int font1_nr = FONT_TEXT_2;
2677   int font2_nr = FONT_TEXT_1;
2678 #endif
2679   int font_nr = font1_nr;
2680   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2681
2682 #if 1
2683   return;       /* !!! USE NEW STUFF !!! */
2684 #endif
2685
2686   if (PANEL_DEACTIVATED(pos))
2687     return;
2688
2689   if (use_dynamic_chars)                /* use dynamic number of chars */
2690   {
2691     chars   = (value < 1000 ? chars1   : chars2);
2692     font_nr = (value < 1000 ? font1_nr : font2_nr);
2693   }
2694
2695   /* clear background if value just changed its size (dynamic chars only) */
2696   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2697   {
2698     int width1 = chars1 * getFontWidth(font1_nr);
2699     int width2 = chars2 * getFontWidth(font2_nr);
2700     int max_width = MAX(width1, width2);
2701     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2702
2703     pos->width = max_width;
2704
2705     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2706                                max_width, max_height);
2707   }
2708
2709   pos->width = chars * getFontWidth(font_nr);
2710
2711   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2712
2713   last_value = value;
2714 }
2715
2716 void DrawGameValue_Level(int value)
2717 {
2718   struct TextPosInfo *pos = &game.panel.level_number;
2719   int chars1 = 2;
2720   int chars2 = 3;
2721   int chars = pos->size;
2722 #if 1
2723   int font1_nr = pos->font;
2724   int font2_nr = pos->font_alt;
2725 #else
2726   int font1_nr = FONT_TEXT_2;
2727   int font2_nr = FONT_TEXT_1;
2728 #endif
2729   int font_nr = font1_nr;
2730   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2731
2732 #if 1
2733   return;       /* !!! USE NEW STUFF !!! */
2734 #endif
2735
2736   if (PANEL_DEACTIVATED(pos))
2737     return;
2738
2739   if (use_dynamic_chars)                /* use dynamic number of chars */
2740   {
2741     chars   = (level_nr < 100 ? chars1   : chars2);
2742     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2743   }
2744
2745   pos->width = chars * getFontWidth(font_nr);
2746
2747   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2748 }
2749
2750 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2751 {
2752 #if 0
2753   struct TextPosInfo *pos = &game.panel.keys;
2754 #endif
2755 #if 0
2756   int base_key_graphic = EL_KEY_1;
2757 #endif
2758   int i;
2759
2760 #if 1
2761   return;       /* !!! USE NEW STUFF !!! */
2762 #endif
2763
2764 #if 0
2765   if (PANEL_DEACTIVATED(pos))
2766     return;
2767 #endif
2768
2769 #if 0
2770   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2771     base_key_graphic = EL_EM_KEY_1;
2772 #endif
2773
2774 #if 0
2775   pos->width = 4 * MINI_TILEX;
2776 #endif
2777
2778 #if 1
2779   for (i = 0; i < MAX_NUM_KEYS; i++)
2780 #else
2781   /* currently only 4 of 8 possible keys are displayed */
2782   for (i = 0; i < STD_NUM_KEYS; i++)
2783 #endif
2784   {
2785 #if 1
2786     struct TextPosInfo *pos = &game.panel.key[i];
2787 #endif
2788     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2789     int src_y = DOOR_GFX_PAGEY1 + 123;
2790 #if 1
2791     int dst_x = PANEL_XPOS(pos);
2792     int dst_y = PANEL_YPOS(pos);
2793 #else
2794     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2795     int dst_y = PANEL_YPOS(pos);
2796 #endif
2797
2798 #if 1
2799     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2800                    level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2801                    EL_KEY_1) + i;
2802     int graphic = el2edimg(element);
2803 #endif
2804
2805 #if 1
2806     if (PANEL_DEACTIVATED(pos))
2807       continue;
2808 #endif
2809
2810 #if 0
2811     /* masked blit with tiles from half-size scaled bitmap does not work yet
2812        (no mask bitmap created for these sizes after loading and scaling) --
2813        solution: load without creating mask, scale, then create final mask */
2814
2815     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2816                MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2817
2818     if (key[i])
2819     {
2820 #if 0
2821       int graphic = el2edimg(base_key_graphic + i);
2822 #endif
2823       Bitmap *src_bitmap;
2824       int src_x, src_y;
2825
2826       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2827
2828       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2829                     dst_x - src_x, dst_y - src_y);
2830       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2831                        dst_x, dst_y);
2832     }
2833 #else
2834 #if 1
2835     if (key[i])
2836       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2837     else
2838       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2839                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2840 #else
2841     if (key[i])
2842       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
2843     else
2844       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2845                  MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2846 #endif
2847 #endif
2848   }
2849 }
2850
2851 #else
2852
2853 void DrawGameValue_Emeralds(int value)
2854 {
2855   int font_nr = FONT_TEXT_2;
2856   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2857
2858   if (PANEL_DEACTIVATED(game.panel.gems))
2859     return;
2860
2861   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
2862 }
2863
2864 void DrawGameValue_Dynamite(int value)
2865 {
2866   int font_nr = FONT_TEXT_2;
2867   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
2868
2869   if (PANEL_DEACTIVATED(game.panel.inventory_count))
2870     return;
2871
2872   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
2873 }
2874
2875 void DrawGameValue_Score(int value)
2876 {
2877   int font_nr = FONT_TEXT_2;
2878   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
2879
2880   if (PANEL_DEACTIVATED(game.panel.score))
2881     return;
2882
2883   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
2884 }
2885
2886 void DrawGameValue_Time(int value)
2887 {
2888   int font1_nr = FONT_TEXT_2;
2889 #if 1
2890   int font2_nr = FONT_TEXT_1;
2891 #else
2892   int font2_nr = FONT_LEVEL_NUMBER;
2893 #endif
2894   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
2895   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
2896
2897   if (PANEL_DEACTIVATED(game.panel.time))
2898     return;
2899
2900   /* clear background if value just changed its size */
2901   if (value == 999 || value == 1000)
2902     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
2903
2904   if (value < 1000)
2905     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
2906   else
2907     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
2908 }
2909
2910 void DrawGameValue_Level(int value)
2911 {
2912   int font1_nr = FONT_TEXT_2;
2913 #if 1
2914   int font2_nr = FONT_TEXT_1;
2915 #else
2916   int font2_nr = FONT_LEVEL_NUMBER;
2917 #endif
2918
2919   if (PANEL_DEACTIVATED(game.panel.level))
2920     return;
2921
2922   if (level_nr < 100)
2923     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
2924   else
2925     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
2926 }
2927
2928 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2929 {
2930   int base_key_graphic = EL_KEY_1;
2931   int i;
2932
2933   if (PANEL_DEACTIVATED(game.panel.keys))
2934     return;
2935
2936   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2937     base_key_graphic = EL_EM_KEY_1;
2938
2939   /* currently only 4 of 8 possible keys are displayed */
2940   for (i = 0; i < STD_NUM_KEYS; i++)
2941   {
2942     int x = XX_KEYS + i * MINI_TILEX;
2943     int y = YY_KEYS;
2944
2945     if (key[i])
2946       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
2947     else
2948       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2949                  DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
2950   }
2951 }
2952
2953 #endif
2954
2955 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2956                        int key_bits)
2957 {
2958   int key[MAX_NUM_KEYS];
2959   int i;
2960
2961   /* prevent EM engine from updating time/score values parallel to GameWon() */
2962   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2963       local_player->LevelSolved)
2964     return;
2965
2966   for (i = 0; i < MAX_NUM_KEYS; i++)
2967     key[i] = key_bits & (1 << i);
2968
2969   DrawGameValue_Level(level_nr);
2970
2971   DrawGameValue_Emeralds(emeralds);
2972   DrawGameValue_Dynamite(dynamite);
2973   DrawGameValue_Score(score);
2974   DrawGameValue_Time(time);
2975
2976   DrawGameValue_Keys(key);
2977 }
2978
2979 void UpdateGameDoorValues()
2980 {
2981   UpdateGameControlValues();
2982 }
2983
2984 void DrawGameDoorValues()
2985 {
2986   DisplayGameControlValues();
2987 }
2988
2989 void DrawGameDoorValues_OLD()
2990 {
2991   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
2992   int dynamite_value = 0;
2993   int score_value = (local_player->LevelSolved ? local_player->score_final :
2994                      local_player->score);
2995   int gems_value = local_player->gems_still_needed;
2996   int key_bits = 0;
2997   int i, j;
2998
2999   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3000   {
3001     DrawGameDoorValues_EM();
3002
3003     return;
3004   }
3005
3006   if (game.centered_player_nr == -1)
3007   {
3008     for (i = 0; i < MAX_PLAYERS; i++)
3009     {
3010       for (j = 0; j < MAX_NUM_KEYS; j++)
3011         if (stored_player[i].key[j])
3012           key_bits |= (1 << j);
3013
3014       dynamite_value += stored_player[i].inventory_size;
3015     }
3016   }
3017   else
3018   {
3019     int player_nr = game.centered_player_nr;
3020
3021     for (i = 0; i < MAX_NUM_KEYS; i++)
3022       if (stored_player[player_nr].key[i])
3023         key_bits |= (1 << i);
3024
3025     dynamite_value = stored_player[player_nr].inventory_size;
3026   }
3027
3028   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3029                     key_bits);
3030 }
3031
3032
3033 /*
3034   =============================================================================
3035   InitGameEngine()
3036   -----------------------------------------------------------------------------
3037   initialize game engine due to level / tape version number
3038   =============================================================================
3039 */
3040
3041 static void InitGameEngine()
3042 {
3043   int i, j, k, l, x, y;
3044
3045   /* set game engine from tape file when re-playing, else from level file */
3046   game.engine_version = (tape.playing ? tape.engine_version :
3047                          level.game_version);
3048
3049   /* ---------------------------------------------------------------------- */
3050   /* set flags for bugs and changes according to active game engine version */
3051   /* ---------------------------------------------------------------------- */
3052
3053   /*
3054     Summary of bugfix/change:
3055     Fixed handling for custom elements that change when pushed by the player.
3056
3057     Fixed/changed in version:
3058     3.1.0
3059
3060     Description:
3061     Before 3.1.0, custom elements that "change when pushing" changed directly
3062     after the player started pushing them (until then handled in "DigField()").
3063     Since 3.1.0, these custom elements are not changed until the "pushing"
3064     move of the element is finished (now handled in "ContinueMoving()").
3065
3066     Affected levels/tapes:
3067     The first condition is generally needed for all levels/tapes before version
3068     3.1.0, which might use the old behaviour before it was changed; known tapes
3069     that are affected are some tapes from the level set "Walpurgis Gardens" by
3070     Jamie Cullen.
3071     The second condition is an exception from the above case and is needed for
3072     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3073     above (including some development versions of 3.1.0), but before it was
3074     known that this change would break tapes like the above and was fixed in
3075     3.1.1, so that the changed behaviour was active although the engine version
3076     while recording maybe was before 3.1.0. There is at least one tape that is
3077     affected by this exception, which is the tape for the one-level set "Bug
3078     Machine" by Juergen Bonhagen.
3079   */
3080
3081   game.use_change_when_pushing_bug =
3082     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3083      !(tape.playing &&
3084        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3085        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3086
3087   /*
3088     Summary of bugfix/change:
3089     Fixed handling for blocking the field the player leaves when moving.
3090
3091     Fixed/changed in version:
3092     3.1.1
3093
3094     Description:
3095     Before 3.1.1, when "block last field when moving" was enabled, the field
3096     the player is leaving when moving was blocked for the time of the move,
3097     and was directly unblocked afterwards. This resulted in the last field
3098     being blocked for exactly one less than the number of frames of one player
3099     move. Additionally, even when blocking was disabled, the last field was
3100     blocked for exactly one frame.
3101     Since 3.1.1, due to changes in player movement handling, the last field
3102     is not blocked at all when blocking is disabled. When blocking is enabled,
3103     the last field is blocked for exactly the number of frames of one player
3104     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3105     last field is blocked for exactly one more than the number of frames of
3106     one player move.
3107
3108     Affected levels/tapes:
3109     (!!! yet to be determined -- probably many !!!)
3110   */
3111
3112   game.use_block_last_field_bug =
3113     (game.engine_version < VERSION_IDENT(3,1,1,0));
3114
3115   /*
3116     Summary of bugfix/change:
3117     Changed behaviour of CE changes with multiple changes per single frame.
3118
3119     Fixed/changed in version:
3120     3.2.0-6
3121
3122     Description:
3123     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3124     This resulted in race conditions where CEs seem to behave strange in some
3125     situations (where triggered CE changes were just skipped because there was
3126     already a CE change on that tile in the playfield in that engine frame).
3127     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3128     (The number of changes per frame must be limited in any case, because else
3129     it is easily possible to define CE changes that would result in an infinite
3130     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3131     should be set large enough so that it would only be reached in cases where
3132     the corresponding CE change conditions run into a loop. Therefore, it seems
3133     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3134     maximal number of change pages for custom elements.)
3135
3136     Affected levels/tapes:
3137     Probably many.
3138   */
3139
3140 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3141   game.max_num_changes_per_frame = 1;
3142 #else
3143   game.max_num_changes_per_frame =
3144     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3145 #endif
3146
3147   /* ---------------------------------------------------------------------- */
3148
3149   /* default scan direction: scan playfield from top/left to bottom/right */
3150   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3151
3152   /* dynamically adjust element properties according to game engine version */
3153   InitElementPropertiesEngine(game.engine_version);
3154
3155 #if 0
3156   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3157   printf("          tape version == %06d [%s] [file: %06d]\n",
3158          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3159          tape.file_version);
3160   printf("       => game.engine_version == %06d\n", game.engine_version);
3161 #endif
3162
3163   /* ---------- initialize player's initial move delay --------------------- */
3164
3165   /* dynamically adjust player properties according to level information */
3166   for (i = 0; i < MAX_PLAYERS; i++)
3167     game.initial_move_delay_value[i] =
3168       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3169
3170   /* dynamically adjust player properties according to game engine version */
3171   for (i = 0; i < MAX_PLAYERS; i++)
3172     game.initial_move_delay[i] =
3173       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3174        game.initial_move_delay_value[i] : 0);
3175
3176   /* ---------- initialize player's initial push delay --------------------- */
3177
3178   /* dynamically adjust player properties according to game engine version */
3179   game.initial_push_delay_value =
3180     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3181
3182   /* ---------- initialize changing elements ------------------------------- */
3183
3184   /* initialize changing elements information */
3185   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3186   {
3187     struct ElementInfo *ei = &element_info[i];
3188
3189     /* this pointer might have been changed in the level editor */
3190     ei->change = &ei->change_page[0];
3191
3192     if (!IS_CUSTOM_ELEMENT(i))
3193     {
3194       ei->change->target_element = EL_EMPTY_SPACE;
3195       ei->change->delay_fixed = 0;
3196       ei->change->delay_random = 0;
3197       ei->change->delay_frames = 1;
3198     }
3199
3200     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3201     {
3202       ei->has_change_event[j] = FALSE;
3203
3204       ei->event_page_nr[j] = 0;
3205       ei->event_page[j] = &ei->change_page[0];
3206     }
3207   }
3208
3209   /* add changing elements from pre-defined list */
3210   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3211   {
3212     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3213     struct ElementInfo *ei = &element_info[ch_delay->element];
3214
3215     ei->change->target_element       = ch_delay->target_element;
3216     ei->change->delay_fixed          = ch_delay->change_delay;
3217
3218     ei->change->pre_change_function  = ch_delay->pre_change_function;
3219     ei->change->change_function      = ch_delay->change_function;
3220     ei->change->post_change_function = ch_delay->post_change_function;
3221
3222     ei->change->can_change = TRUE;
3223     ei->change->can_change_or_has_action = TRUE;
3224
3225     ei->has_change_event[CE_DELAY] = TRUE;
3226
3227     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3228     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3229   }
3230
3231   /* ---------- initialize internal run-time variables ------------- */
3232
3233   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3234   {
3235     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3236
3237     for (j = 0; j < ei->num_change_pages; j++)
3238     {
3239       ei->change_page[j].can_change_or_has_action =
3240         (ei->change_page[j].can_change |
3241          ei->change_page[j].has_action);
3242     }
3243   }
3244
3245   /* add change events from custom element configuration */
3246   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3247   {
3248     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3249
3250     for (j = 0; j < ei->num_change_pages; j++)
3251     {
3252       if (!ei->change_page[j].can_change_or_has_action)
3253         continue;
3254
3255       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3256       {
3257         /* only add event page for the first page found with this event */
3258         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3259         {
3260           ei->has_change_event[k] = TRUE;
3261
3262           ei->event_page_nr[k] = j;
3263           ei->event_page[k] = &ei->change_page[j];
3264         }
3265       }
3266     }
3267   }
3268
3269   /* ---------- initialize run-time trigger player and element ------------- */
3270
3271   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3272   {
3273     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3274
3275     for (j = 0; j < ei->num_change_pages; j++)
3276     {
3277       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3278       ei->change_page[j].actual_trigger_player = EL_PLAYER_1;
3279       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3280       ei->change_page[j].actual_trigger_ce_value = 0;
3281       ei->change_page[j].actual_trigger_ce_score = 0;
3282     }
3283   }
3284
3285   /* ---------- initialize trigger events ---------------------------------- */
3286
3287   /* initialize trigger events information */
3288   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3289     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3290       trigger_events[i][j] = FALSE;
3291
3292   /* add trigger events from element change event properties */
3293   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       if (!ei->change_page[j].can_change_or_has_action)
3300         continue;
3301
3302       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3303       {
3304         int trigger_element = ei->change_page[j].trigger_element;
3305
3306         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3307         {
3308           if (ei->change_page[j].has_event[k])
3309           {
3310             if (IS_GROUP_ELEMENT(trigger_element))
3311             {
3312               struct ElementGroupInfo *group =
3313                 element_info[trigger_element].group;
3314
3315               for (l = 0; l < group->num_elements_resolved; l++)
3316                 trigger_events[group->element_resolved[l]][k] = TRUE;
3317             }
3318             else if (trigger_element == EL_ANY_ELEMENT)
3319               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3320                 trigger_events[l][k] = TRUE;
3321             else
3322               trigger_events[trigger_element][k] = TRUE;
3323           }
3324         }
3325       }
3326     }
3327   }
3328
3329   /* ---------- initialize push delay -------------------------------------- */
3330
3331   /* initialize push delay values to default */
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333   {
3334     if (!IS_CUSTOM_ELEMENT(i))
3335     {
3336       /* set default push delay values (corrected since version 3.0.7-1) */
3337       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3338       {
3339         element_info[i].push_delay_fixed = 2;
3340         element_info[i].push_delay_random = 8;
3341       }
3342       else
3343       {
3344         element_info[i].push_delay_fixed = 8;
3345         element_info[i].push_delay_random = 8;
3346       }
3347     }
3348   }
3349
3350   /* set push delay value for certain elements from pre-defined list */
3351   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3352   {
3353     int e = push_delay_list[i].element;
3354
3355     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3356     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3357   }
3358
3359   /* set push delay value for Supaplex elements for newer engine versions */
3360   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3361   {
3362     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3363     {
3364       if (IS_SP_ELEMENT(i))
3365       {
3366         /* set SP push delay to just enough to push under a falling zonk */
3367         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3368
3369         element_info[i].push_delay_fixed  = delay;
3370         element_info[i].push_delay_random = 0;
3371       }
3372     }
3373   }
3374
3375   /* ---------- initialize move stepsize ----------------------------------- */
3376
3377   /* initialize move stepsize values to default */
3378   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3379     if (!IS_CUSTOM_ELEMENT(i))
3380       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3381
3382   /* set move stepsize value for certain elements from pre-defined list */
3383   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3384   {
3385     int e = move_stepsize_list[i].element;
3386
3387     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3388   }
3389
3390   /* ---------- initialize collect score ----------------------------------- */
3391
3392   /* initialize collect score values for custom elements from initial value */
3393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3394     if (IS_CUSTOM_ELEMENT(i))
3395       element_info[i].collect_score = element_info[i].collect_score_initial;
3396
3397   /* ---------- initialize collect count ----------------------------------- */
3398
3399   /* initialize collect count values for non-custom elements */
3400   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3401     if (!IS_CUSTOM_ELEMENT(i))
3402       element_info[i].collect_count_initial = 0;
3403
3404   /* add collect count values for all elements from pre-defined list */
3405   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3406     element_info[collect_count_list[i].element].collect_count_initial =
3407       collect_count_list[i].count;
3408
3409   /* ---------- initialize access direction -------------------------------- */
3410
3411   /* initialize access direction values to default (access from every side) */
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3415
3416   /* set access direction value for certain elements from pre-defined list */
3417   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3418     element_info[access_direction_list[i].element].access_direction =
3419       access_direction_list[i].direction;
3420
3421   /* ---------- initialize explosion content ------------------------------- */
3422   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3423   {
3424     if (IS_CUSTOM_ELEMENT(i))
3425       continue;
3426
3427     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3428     {
3429       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3430
3431       element_info[i].content.e[x][y] =
3432         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3433          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3434          i == EL_PLAYER_3 ? EL_EMERALD :
3435          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3436          i == EL_MOLE ? EL_EMERALD_RED :
3437          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3438          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3439          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3440          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3441          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3442          i == EL_WALL_EMERALD ? EL_EMERALD :
3443          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3444          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3445          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3446          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3447          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3448          i == EL_WALL_PEARL ? EL_PEARL :
3449          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3450          EL_EMPTY);
3451     }
3452   }
3453
3454   /* ---------- initialize recursion detection ------------------------------ */
3455   recursion_loop_depth = 0;
3456   recursion_loop_detected = FALSE;
3457   recursion_loop_element = EL_UNDEFINED;
3458
3459   /* ---------- initialize graphics engine ---------------------------------- */
3460   game.scroll_delay_value =
3461     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3462      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3463   game.scroll_delay_value =
3464     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3465 }
3466
3467 int get_num_special_action(int element, int action_first, int action_last)
3468 {
3469   int num_special_action = 0;
3470   int i, j;
3471
3472   for (i = action_first; i <= action_last; i++)
3473   {
3474     boolean found = FALSE;
3475
3476     for (j = 0; j < NUM_DIRECTIONS; j++)
3477       if (el_act_dir2img(element, i, j) !=
3478           el_act_dir2img(element, ACTION_DEFAULT, j))
3479         found = TRUE;
3480
3481     if (found)
3482       num_special_action++;
3483     else
3484       break;
3485   }
3486
3487   return num_special_action;
3488 }
3489
3490
3491 /*
3492   =============================================================================
3493   InitGame()
3494   -----------------------------------------------------------------------------
3495   initialize and start new game
3496   =============================================================================
3497 */
3498
3499 void InitGame()
3500 {
3501   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3502   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3503   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3504 #if 0
3505   boolean do_fading = (game_status == GAME_MODE_MAIN);
3506 #endif
3507   int i, j, x, y;
3508
3509   game_status = GAME_MODE_PLAYING;
3510
3511   InitGameEngine();
3512   InitGameControlValues();
3513
3514   /* don't play tapes over network */
3515   network_playing = (options.network && !tape.playing);
3516
3517   for (i = 0; i < MAX_PLAYERS; i++)
3518   {
3519     struct PlayerInfo *player = &stored_player[i];
3520
3521     player->index_nr = i;
3522     player->index_bit = (1 << i);
3523     player->element_nr = EL_PLAYER_1 + i;
3524
3525     player->present = FALSE;
3526     player->active = FALSE;
3527     player->killed = FALSE;
3528
3529     player->action = 0;
3530     player->effective_action = 0;
3531     player->programmed_action = 0;
3532
3533     player->score = 0;
3534     player->score_final = 0;
3535
3536     player->gems_still_needed = level.gems_needed;
3537     player->sokobanfields_still_needed = 0;
3538     player->lights_still_needed = 0;
3539     player->friends_still_needed = 0;
3540
3541     for (j = 0; j < MAX_NUM_KEYS; j++)
3542       player->key[j] = FALSE;
3543
3544     player->num_white_keys = 0;
3545
3546     player->dynabomb_count = 0;
3547     player->dynabomb_size = 1;
3548     player->dynabombs_left = 0;
3549     player->dynabomb_xl = FALSE;
3550
3551     player->MovDir = MV_NONE;
3552     player->MovPos = 0;
3553     player->GfxPos = 0;
3554     player->GfxDir = MV_NONE;
3555     player->GfxAction = ACTION_DEFAULT;
3556     player->Frame = 0;
3557     player->StepFrame = 0;
3558
3559     player->use_murphy = FALSE;
3560     player->artwork_element =
3561       (level.use_artwork_element[i] ? level.artwork_element[i] :
3562        player->element_nr);
3563
3564     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3565     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3566
3567     player->gravity = level.initial_player_gravity[i];
3568
3569     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3570
3571     player->actual_frame_counter = 0;
3572
3573     player->step_counter = 0;
3574
3575     player->last_move_dir = MV_NONE;
3576
3577     player->is_active = FALSE;
3578
3579     player->is_waiting = FALSE;
3580     player->is_moving = FALSE;
3581     player->is_auto_moving = FALSE;
3582     player->is_digging = FALSE;
3583     player->is_snapping = FALSE;
3584     player->is_collecting = FALSE;
3585     player->is_pushing = FALSE;
3586     player->is_switching = FALSE;
3587     player->is_dropping = FALSE;
3588     player->is_dropping_pressed = FALSE;
3589
3590     player->is_bored = FALSE;
3591     player->is_sleeping = FALSE;
3592
3593     player->frame_counter_bored = -1;
3594     player->frame_counter_sleeping = -1;
3595
3596     player->anim_delay_counter = 0;
3597     player->post_delay_counter = 0;
3598
3599     player->dir_waiting = MV_NONE;
3600     player->action_waiting = ACTION_DEFAULT;
3601     player->last_action_waiting = ACTION_DEFAULT;
3602     player->special_action_bored = ACTION_DEFAULT;
3603     player->special_action_sleeping = ACTION_DEFAULT;
3604
3605     player->switch_x = -1;
3606     player->switch_y = -1;
3607
3608     player->drop_x = -1;
3609     player->drop_y = -1;
3610
3611     player->show_envelope = 0;
3612
3613     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3614
3615     player->push_delay       = -1;      /* initialized when pushing starts */
3616     player->push_delay_value = game.initial_push_delay_value;
3617
3618     player->drop_delay = 0;
3619     player->drop_pressed_delay = 0;
3620
3621     player->last_jx = -1;
3622     player->last_jy = -1;
3623     player->jx = -1;
3624     player->jy = -1;
3625
3626     player->shield_normal_time_left = 0;
3627     player->shield_deadly_time_left = 0;
3628
3629     player->inventory_infinite_element = EL_UNDEFINED;
3630     player->inventory_size = 0;
3631
3632     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3633     SnapField(player, 0, 0);
3634
3635     player->LevelSolved = FALSE;
3636     player->GameOver = FALSE;
3637
3638     player->LevelSolved_GameWon = FALSE;
3639     player->LevelSolved_GameEnd = FALSE;
3640     player->LevelSolved_PanelOff = FALSE;
3641     player->LevelSolved_SaveTape = FALSE;
3642     player->LevelSolved_SaveScore = FALSE;
3643     player->LevelSolved_CountingTime = 0;
3644     player->LevelSolved_CountingScore = 0;
3645   }
3646
3647   network_player_action_received = FALSE;
3648
3649 #if defined(NETWORK_AVALIABLE)
3650   /* initial null action */
3651   if (network_playing)
3652     SendToServer_MovePlayer(MV_NONE);
3653 #endif
3654
3655   ZX = ZY = -1;
3656   ExitX = ExitY = -1;
3657
3658   FrameCounter = 0;
3659   TimeFrames = 0;
3660   TimePlayed = 0;
3661   TimeLeft = level.time;
3662   TapeTime = 0;
3663
3664   ScreenMovDir = MV_NONE;
3665   ScreenMovPos = 0;
3666   ScreenGfxPos = 0;
3667
3668   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3669
3670   AllPlayersGone = FALSE;
3671
3672   game.yamyam_content_nr = 0;
3673   game.robot_wheel_active = FALSE;
3674   game.magic_wall_active = FALSE;
3675   game.magic_wall_time_left = 0;
3676   game.light_time_left = 0;
3677   game.timegate_time_left = 0;
3678   game.switchgate_pos = 0;
3679   game.wind_direction = level.wind_direction_initial;
3680
3681 #if !USE_PLAYER_GRAVITY
3682   game.gravity = FALSE;
3683   game.explosions_delayed = TRUE;
3684 #endif
3685
3686   game.lenses_time_left = 0;
3687   game.magnify_time_left = 0;
3688
3689   game.ball_state = level.ball_state_initial;
3690   game.ball_content_nr = 0;
3691
3692   game.envelope_active = FALSE;
3693
3694   /* set focus to local player for network games, else to all players */
3695   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3696   game.centered_player_nr_next = game.centered_player_nr;
3697   game.set_centered_player = FALSE;
3698
3699   if (network_playing && tape.recording)
3700   {
3701     /* store client dependent player focus when recording network games */
3702     tape.centered_player_nr_next = game.centered_player_nr_next;
3703     tape.set_centered_player = TRUE;
3704   }
3705
3706   for (i = 0; i < NUM_BELTS; i++)
3707   {
3708     game.belt_dir[i] = MV_NONE;
3709     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3710   }
3711
3712   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3713     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3714
3715   SCAN_PLAYFIELD(x, y)
3716   {
3717     Feld[x][y] = level.field[x][y];
3718     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3719     ChangeDelay[x][y] = 0;
3720     ChangePage[x][y] = -1;
3721 #if USE_NEW_CUSTOM_VALUE
3722     CustomValue[x][y] = 0;              /* initialized in InitField() */
3723 #endif
3724     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3725     AmoebaNr[x][y] = 0;
3726     WasJustMoving[x][y] = 0;
3727     WasJustFalling[x][y] = 0;
3728     CheckCollision[x][y] = 0;
3729     CheckImpact[x][y] = 0;
3730     Stop[x][y] = FALSE;
3731     Pushed[x][y] = FALSE;
3732
3733     ChangeCount[x][y] = 0;
3734     ChangeEvent[x][y] = -1;
3735
3736     ExplodePhase[x][y] = 0;
3737     ExplodeDelay[x][y] = 0;
3738     ExplodeField[x][y] = EX_TYPE_NONE;
3739
3740     RunnerVisit[x][y] = 0;
3741     PlayerVisit[x][y] = 0;
3742
3743     GfxFrame[x][y] = 0;
3744     GfxRandom[x][y] = INIT_GFX_RANDOM();
3745     GfxElement[x][y] = EL_UNDEFINED;
3746     GfxAction[x][y] = ACTION_DEFAULT;
3747     GfxDir[x][y] = MV_NONE;
3748   }
3749
3750   SCAN_PLAYFIELD(x, y)
3751   {
3752     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3753       emulate_bd = FALSE;
3754     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3755       emulate_sb = FALSE;
3756     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3757       emulate_sp = FALSE;
3758
3759     InitField(x, y, TRUE);
3760
3761     ResetGfxAnimation(x, y);
3762   }
3763
3764   InitBeltMovement();
3765
3766   for (i = 0; i < MAX_PLAYERS; i++)
3767   {
3768     struct PlayerInfo *player = &stored_player[i];
3769
3770     /* set number of special actions for bored and sleeping animation */
3771     player->num_special_action_bored =
3772       get_num_special_action(player->artwork_element,
3773                              ACTION_BORING_1, ACTION_BORING_LAST);
3774     player->num_special_action_sleeping =
3775       get_num_special_action(player->artwork_element,
3776                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3777   }
3778
3779   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3780                     emulate_sb ? EMU_SOKOBAN :
3781                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3782
3783 #if USE_NEW_ALL_SLIPPERY
3784   /* initialize type of slippery elements */
3785   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3786   {
3787     if (!IS_CUSTOM_ELEMENT(i))
3788     {
3789       /* default: elements slip down either to the left or right randomly */
3790       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3791
3792       /* SP style elements prefer to slip down on the left side */
3793       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3794         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3795
3796       /* BD style elements prefer to slip down on the left side */
3797       if (game.emulation == EMU_BOULDERDASH)
3798         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3799     }
3800   }
3801 #endif
3802
3803   /* initialize explosion and ignition delay */
3804   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3805   {
3806     if (!IS_CUSTOM_ELEMENT(i))
3807     {
3808       int num_phase = 8;
3809       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3810                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3811                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3812       int last_phase = (num_phase + 1) * delay;
3813       int half_phase = (num_phase / 2) * delay;
3814
3815       element_info[i].explosion_delay = last_phase - 1;
3816       element_info[i].ignition_delay = half_phase;
3817
3818       if (i == EL_BLACK_ORB)
3819         element_info[i].ignition_delay = 1;
3820     }
3821
3822 #if 0
3823     if (element_info[i].explosion_delay < 1)    /* !!! check again !!! */
3824       element_info[i].explosion_delay = 1;
3825
3826     if (element_info[i].ignition_delay < 1)     /* !!! check again !!! */
3827       element_info[i].ignition_delay = 1;
3828 #endif
3829   }
3830
3831   /* correct non-moving belts to start moving left */
3832   for (i = 0; i < NUM_BELTS; i++)
3833     if (game.belt_dir[i] == MV_NONE)
3834       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3835
3836   /* check if any connected player was not found in playfield */
3837   for (i = 0; i < MAX_PLAYERS; i++)
3838   {
3839     struct PlayerInfo *player = &stored_player[i];
3840
3841     if (player->connected && !player->present)
3842     {
3843       for (j = 0; j < MAX_PLAYERS; j++)
3844       {
3845         struct PlayerInfo *some_player = &stored_player[j];
3846         int jx = some_player->jx, jy = some_player->jy;
3847
3848         /* assign first free player found that is present in the playfield */
3849         if (some_player->present && !some_player->connected)
3850         {
3851           player->present = TRUE;
3852           player->active = TRUE;
3853
3854           some_player->present = FALSE;
3855           some_player->active = FALSE;
3856
3857           player->artwork_element = some_player->artwork_element;
3858
3859           player->block_last_field       = some_player->block_last_field;
3860           player->block_delay_adjustment = some_player->block_delay_adjustment;
3861
3862           StorePlayer[jx][jy] = player->element_nr;
3863           player->jx = player->last_jx = jx;
3864           player->jy = player->last_jy = jy;
3865
3866           break;
3867         }
3868       }
3869     }
3870   }
3871
3872   if (tape.playing)
3873   {
3874     /* when playing a tape, eliminate all players who do not participate */
3875
3876     for (i = 0; i < MAX_PLAYERS; i++)
3877     {
3878       if (stored_player[i].active && !tape.player_participates[i])
3879       {
3880         struct PlayerInfo *player = &stored_player[i];
3881         int jx = player->jx, jy = player->jy;
3882
3883         player->active = FALSE;
3884         StorePlayer[jx][jy] = 0;
3885         Feld[jx][jy] = EL_EMPTY;
3886       }
3887     }
3888   }
3889   else if (!options.network && !setup.team_mode)        /* && !tape.playing */
3890   {
3891     /* when in single player mode, eliminate all but the first active player */
3892
3893     for (i = 0; i < MAX_PLAYERS; i++)
3894     {
3895       if (stored_player[i].active)
3896       {
3897         for (j = i + 1; j < MAX_PLAYERS; j++)
3898         {
3899           if (stored_player[j].active)
3900           {
3901             struct PlayerInfo *player = &stored_player[j];
3902             int jx = player->jx, jy = player->jy;
3903
3904             player->active = FALSE;
3905             player->present = FALSE;
3906
3907             StorePlayer[jx][jy] = 0;
3908             Feld[jx][jy] = EL_EMPTY;
3909           }
3910         }
3911       }
3912     }
3913   }
3914
3915   /* when recording the game, store which players take part in the game */
3916   if (tape.recording)
3917   {
3918     for (i = 0; i < MAX_PLAYERS; i++)
3919       if (stored_player[i].active)
3920         tape.player_participates[i] = TRUE;
3921   }
3922
3923   if (options.debug)
3924   {
3925     for (i = 0; i < MAX_PLAYERS; i++)
3926     {
3927       struct PlayerInfo *player = &stored_player[i];
3928
3929       printf("Player %d: present == %d, connected == %d, active == %d.\n",
3930              i+1,
3931              player->present,
3932              player->connected,
3933              player->active);
3934       if (local_player == player)
3935         printf("Player  %d is local player.\n", i+1);
3936     }
3937   }
3938
3939   if (BorderElement == EL_EMPTY)
3940   {
3941     SBX_Left = 0;
3942     SBX_Right = lev_fieldx - SCR_FIELDX;
3943     SBY_Upper = 0;
3944     SBY_Lower = lev_fieldy - SCR_FIELDY;
3945   }
3946   else
3947   {
3948     SBX_Left = -1;
3949     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3950     SBY_Upper = -1;
3951     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3952   }
3953
3954   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
3955     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3956
3957   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
3958     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3959
3960   /* if local player not found, look for custom element that might create
3961      the player (make some assumptions about the right custom element) */
3962   if (!local_player->present)
3963   {
3964     int start_x = 0, start_y = 0;
3965     int found_rating = 0;
3966     int found_element = EL_UNDEFINED;
3967     int player_nr = local_player->index_nr;
3968
3969     SCAN_PLAYFIELD(x, y)
3970     {
3971       int element = Feld[x][y];
3972       int content;
3973       int xx, yy;
3974       boolean is_player;
3975
3976       if (level.use_start_element[player_nr] &&
3977           level.start_element[player_nr] == element &&
3978           found_rating < 4)
3979       {
3980         start_x = x;
3981         start_y = y;
3982
3983         found_rating = 4;
3984         found_element = element;
3985       }
3986
3987       if (!IS_CUSTOM_ELEMENT(element))
3988         continue;
3989
3990       if (CAN_CHANGE(element))
3991       {
3992         for (i = 0; i < element_info[element].num_change_pages; i++)
3993         {
3994           /* check for player created from custom element as single target */
3995           content = element_info[element].change_page[i].target_element;
3996           is_player = ELEM_IS_PLAYER(content);
3997
3998           if (is_player && (found_rating < 3 ||
3999                             (found_rating == 3 && element < found_element)))
4000           {
4001             start_x = x;
4002             start_y = y;
4003
4004             found_rating = 3;
4005             found_element = element;
4006           }
4007         }
4008       }
4009
4010       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4011       {
4012         /* check for player created from custom element as explosion content */
4013         content = element_info[element].content.e[xx][yy];
4014         is_player = ELEM_IS_PLAYER(content);
4015
4016         if (is_player && (found_rating < 2 ||
4017                           (found_rating == 2 && element < found_element)))
4018         {
4019           start_x = x + xx - 1;
4020           start_y = y + yy - 1;
4021
4022           found_rating = 2;
4023           found_element = element;
4024         }
4025
4026         if (!CAN_CHANGE(element))
4027           continue;
4028
4029         for (i = 0; i < element_info[element].num_change_pages; i++)
4030         {
4031           /* check for player created from custom element as extended target */
4032           content =
4033             element_info[element].change_page[i].target_content.e[xx][yy];
4034
4035           is_player = ELEM_IS_PLAYER(content);
4036
4037           if (is_player && (found_rating < 1 ||
4038                             (found_rating == 1 && element < found_element)))
4039           {
4040             start_x = x + xx - 1;
4041             start_y = y + yy - 1;
4042
4043             found_rating = 1;
4044             found_element = element;
4045           }
4046         }
4047       }
4048     }
4049
4050     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4051                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4052                 start_x - MIDPOSX);
4053
4054     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4055                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4056                 start_y - MIDPOSY);
4057   }
4058   else
4059   {
4060     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4061                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4062                 local_player->jx - MIDPOSX);
4063
4064     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4065                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4066                 local_player->jy - MIDPOSY);
4067   }
4068
4069   /* do not use PLAYING mask for fading out from main screen */
4070   game_status = GAME_MODE_MAIN;
4071
4072   StopAnimation();
4073
4074   if (!game.restart_level)
4075     CloseDoor(DOOR_CLOSE_1);
4076
4077 #if 1
4078   if (level_editor_test_game)
4079     FadeSkipNextFadeIn();
4080   else
4081     FadeSetEnterScreen();
4082 #else
4083   if (level_editor_test_game)
4084     fading = fading_none;
4085   else
4086     fading = menu.destination;
4087 #endif
4088
4089 #if 1
4090   FadeOut(REDRAW_FIELD);
4091 #else
4092   if (do_fading)
4093     FadeOut(REDRAW_FIELD);
4094 #endif
4095
4096   game_status = GAME_MODE_PLAYING;
4097
4098   /* !!! FIX THIS (START) !!! */
4099   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4100   {
4101     InitGameEngine_EM();
4102
4103     /* blit playfield from scroll buffer to normal back buffer for fading in */
4104     BlitScreenToBitmap_EM(backbuffer);
4105   }
4106   else
4107   {
4108     DrawLevel();
4109     DrawAllPlayers();
4110
4111     /* after drawing the level, correct some elements */
4112     if (game.timegate_time_left == 0)
4113       CloseAllOpenTimegates();
4114
4115     /* blit playfield from scroll buffer to normal back buffer for fading in */
4116     if (setup.soft_scrolling)
4117       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4118
4119     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4120   }
4121   /* !!! FIX THIS (END) !!! */
4122
4123 #if 1
4124   FadeIn(REDRAW_FIELD);
4125 #else
4126   if (do_fading)
4127     FadeIn(REDRAW_FIELD);
4128
4129   BackToFront();
4130 #endif
4131
4132   if (!game.restart_level)
4133   {
4134     /* copy default game door content to main double buffer */
4135     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4136                DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4137   }
4138
4139   SetPanelBackground();
4140   SetDrawBackgroundMask(REDRAW_DOOR_1);
4141
4142   UpdateGameDoorValues();
4143   DrawGameDoorValues();
4144
4145   if (!game.restart_level)
4146   {
4147     UnmapGameButtons();
4148     UnmapTapeButtons();
4149     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4150     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4151     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4152     MapGameButtons();
4153     MapTapeButtons();
4154
4155     /* copy actual game door content to door double buffer for OpenDoor() */
4156     BlitBitmap(drawto, bitmap_db_door,
4157                DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4158
4159     OpenDoor(DOOR_OPEN_ALL);
4160
4161     PlaySound(SND_GAME_STARTING);
4162
4163     if (setup.sound_music)
4164       PlayLevelMusic();
4165
4166     KeyboardAutoRepeatOffUnlessAutoplay();
4167
4168     if (options.debug)
4169     {
4170       for (i = 0; i < MAX_PLAYERS; i++)
4171         printf("Player %d %sactive.\n",
4172                i + 1, (stored_player[i].active ? "" : "not "));
4173     }
4174   }
4175
4176 #if 1
4177   UnmapAllGadgets();
4178
4179   MapGameButtons();
4180   MapTapeButtons();
4181 #endif
4182
4183   game.restart_level = FALSE;
4184 }
4185
4186 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4187 {
4188   /* this is used for non-R'n'D game engines to update certain engine values */
4189
4190   /* needed to determine if sounds are played within the visible screen area */
4191   scroll_x = actual_scroll_x;
4192   scroll_y = actual_scroll_y;
4193 }
4194
4195 void InitMovDir(int x, int y)
4196 {
4197   int i, element = Feld[x][y];
4198   static int xy[4][2] =
4199   {
4200     {  0, +1 },
4201     { +1,  0 },
4202     {  0, -1 },
4203     { -1,  0 }
4204   };
4205   static int direction[3][4] =
4206   {
4207     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4208     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4209     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4210   };
4211
4212   switch (element)
4213   {
4214     case EL_BUG_RIGHT:
4215     case EL_BUG_UP:
4216     case EL_BUG_LEFT:
4217     case EL_BUG_DOWN:
4218       Feld[x][y] = EL_BUG;
4219       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4220       break;
4221
4222     case EL_SPACESHIP_RIGHT:
4223     case EL_SPACESHIP_UP:
4224     case EL_SPACESHIP_LEFT:
4225     case EL_SPACESHIP_DOWN:
4226       Feld[x][y] = EL_SPACESHIP;
4227       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4228       break;
4229
4230     case EL_BD_BUTTERFLY_RIGHT:
4231     case EL_BD_BUTTERFLY_UP:
4232     case EL_BD_BUTTERFLY_LEFT:
4233     case EL_BD_BUTTERFLY_DOWN:
4234       Feld[x][y] = EL_BD_BUTTERFLY;
4235       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4236       break;
4237
4238     case EL_BD_FIREFLY_RIGHT:
4239     case EL_BD_FIREFLY_UP:
4240     case EL_BD_FIREFLY_LEFT:
4241     case EL_BD_FIREFLY_DOWN:
4242       Feld[x][y] = EL_BD_FIREFLY;
4243       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4244       break;
4245
4246     case EL_PACMAN_RIGHT:
4247     case EL_PACMAN_UP:
4248     case EL_PACMAN_LEFT:
4249     case EL_PACMAN_DOWN:
4250       Feld[x][y] = EL_PACMAN;
4251       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4252       break;
4253
4254     case EL_YAMYAM_LEFT:
4255     case EL_YAMYAM_RIGHT:
4256     case EL_YAMYAM_UP:
4257     case EL_YAMYAM_DOWN:
4258       Feld[x][y] = EL_YAMYAM;
4259       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4260       break;
4261
4262     case EL_SP_SNIKSNAK:
4263       MovDir[x][y] = MV_UP;
4264       break;
4265
4266     case EL_SP_ELECTRON:
4267       MovDir[x][y] = MV_LEFT;
4268       break;
4269
4270     case EL_MOLE_LEFT:
4271     case EL_MOLE_RIGHT:
4272     case EL_MOLE_UP:
4273     case EL_MOLE_DOWN:
4274       Feld[x][y] = EL_MOLE;
4275       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4276       break;
4277
4278     default:
4279       if (IS_CUSTOM_ELEMENT(element))
4280       {
4281         struct ElementInfo *ei = &element_info[element];
4282         int move_direction_initial = ei->move_direction_initial;
4283         int move_pattern = ei->move_pattern;
4284
4285         if (move_direction_initial == MV_START_PREVIOUS)
4286         {
4287           if (MovDir[x][y] != MV_NONE)
4288             return;
4289
4290           move_direction_initial = MV_START_AUTOMATIC;
4291         }
4292
4293         if (move_direction_initial == MV_START_RANDOM)
4294           MovDir[x][y] = 1 << RND(4);
4295         else if (move_direction_initial & MV_ANY_DIRECTION)
4296           MovDir[x][y] = move_direction_initial;
4297         else if (move_pattern == MV_ALL_DIRECTIONS ||
4298                  move_pattern == MV_TURNING_LEFT ||
4299                  move_pattern == MV_TURNING_RIGHT ||
4300                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4301                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4302                  move_pattern == MV_TURNING_RANDOM)
4303           MovDir[x][y] = 1 << RND(4);
4304         else if (move_pattern == MV_HORIZONTAL)
4305           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4306         else if (move_pattern == MV_VERTICAL)
4307           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4308         else if (move_pattern & MV_ANY_DIRECTION)
4309           MovDir[x][y] = element_info[element].move_pattern;
4310         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4311                  move_pattern == MV_ALONG_RIGHT_SIDE)
4312         {
4313           /* use random direction as default start direction */
4314           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4315             MovDir[x][y] = 1 << RND(4);
4316
4317           for (i = 0; i < NUM_DIRECTIONS; i++)
4318           {
4319             int x1 = x + xy[i][0];
4320             int y1 = y + xy[i][1];
4321
4322             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4323             {
4324               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4325                 MovDir[x][y] = direction[0][i];
4326               else
4327                 MovDir[x][y] = direction[1][i];
4328
4329               break;
4330             }
4331           }
4332         }                
4333       }
4334       else
4335       {
4336         MovDir[x][y] = 1 << RND(4);
4337
4338         if (element != EL_BUG &&
4339             element != EL_SPACESHIP &&
4340             element != EL_BD_BUTTERFLY &&
4341             element != EL_BD_FIREFLY)
4342           break;
4343
4344         for (i = 0; i < NUM_DIRECTIONS; i++)
4345         {
4346           int x1 = x + xy[i][0];
4347           int y1 = y + xy[i][1];
4348
4349           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4350           {
4351             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4352             {
4353               MovDir[x][y] = direction[0][i];
4354               break;
4355             }
4356             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4357                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4358             {
4359               MovDir[x][y] = direction[1][i];
4360               break;
4361             }
4362           }
4363         }
4364       }
4365       break;
4366   }
4367
4368   GfxDir[x][y] = MovDir[x][y];
4369 }
4370
4371 void InitAmoebaNr(int x, int y)
4372 {
4373   int i;
4374   int group_nr = AmoebeNachbarNr(x, y);
4375
4376   if (group_nr == 0)
4377   {
4378     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4379     {
4380       if (AmoebaCnt[i] == 0)
4381       {
4382         group_nr = i;
4383         break;
4384       }
4385     }
4386   }
4387
4388   AmoebaNr[x][y] = group_nr;
4389   AmoebaCnt[group_nr]++;
4390   AmoebaCnt2[group_nr]++;
4391 }
4392
4393 static void PlayerWins(struct PlayerInfo *player)
4394 {
4395   player->LevelSolved = TRUE;
4396   player->GameOver = TRUE;
4397
4398   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4399                          level.native_em_level->lev->score : player->score);
4400
4401   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4402   player->LevelSolved_CountingScore = player->score_final;
4403 }
4404
4405 void GameWon()
4406 {
4407   static int time, time_final;
4408   static int score, score_final;
4409   static int game_over_delay_1 = 0;
4410   static int game_over_delay_2 = 0;
4411   int game_over_delay_value_1 = 50;
4412   int game_over_delay_value_2 = 50;
4413
4414   if (!local_player->LevelSolved_GameWon)
4415   {
4416     int i;
4417
4418     /* do not start end game actions before the player stops moving (to exit) */
4419     if (local_player->MovPos)
4420       return;
4421
4422     local_player->LevelSolved_GameWon = TRUE;
4423     local_player->LevelSolved_SaveTape = tape.recording;
4424     local_player->LevelSolved_SaveScore = !tape.playing;
4425
4426     if (tape.auto_play)         /* tape might already be stopped here */
4427       tape.auto_play_level_solved = TRUE;
4428
4429 #if 1
4430     TapeStop();
4431 #endif
4432
4433     game_over_delay_1 = game_over_delay_value_1;
4434     game_over_delay_2 = game_over_delay_value_2;
4435
4436     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4437     score = score_final = local_player->score_final;
4438
4439     if (TimeLeft > 0)
4440     {
4441       time_final = 0;
4442       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4443     }
4444     else if (level.time == 0 && TimePlayed < 999)
4445     {
4446       time_final = 999;
4447       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4448     }
4449
4450     local_player->score_final = score_final;
4451
4452     if (level_editor_test_game)
4453     {
4454       time = time_final;
4455       score = score_final;
4456
4457 #if 1
4458       local_player->LevelSolved_CountingTime = time;
4459       local_player->LevelSolved_CountingScore = score;
4460
4461       game_panel_controls[GAME_PANEL_TIME].value = time;
4462       game_panel_controls[GAME_PANEL_SCORE].value = score;
4463
4464       DisplayGameControlValues();
4465 #else
4466       DrawGameValue_Time(time);
4467       DrawGameValue_Score(score);
4468 #endif
4469     }
4470
4471     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4472     {
4473       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4474       {
4475         /* close exit door after last player */
4476         if ((AllPlayersGone &&
4477              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4478               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4479               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4480             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4481             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4482         {
4483           int element = Feld[ExitX][ExitY];
4484
4485 #if 0
4486           if (element == EL_EM_EXIT_OPEN ||
4487               element == EL_EM_STEEL_EXIT_OPEN)
4488           {
4489             Bang(ExitX, ExitY);
4490           }
4491           else
4492 #endif
4493           {
4494             Feld[ExitX][ExitY] =
4495               (element == EL_EXIT_OPEN          ? EL_EXIT_CLOSING :
4496                element == EL_EM_EXIT_OPEN       ? EL_EM_EXIT_CLOSING :
4497                element == EL_SP_EXIT_OPEN       ? EL_SP_EXIT_CLOSING:
4498                element == EL_STEEL_EXIT_OPEN    ? EL_STEEL_EXIT_CLOSING:
4499                EL_EM_STEEL_EXIT_CLOSING);
4500
4501             PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4502           }
4503         }
4504
4505         /* player disappears */
4506         DrawLevelField(ExitX, ExitY);
4507       }
4508
4509       for (i = 0; i < MAX_PLAYERS; i++)
4510       {
4511         struct PlayerInfo *player = &stored_player[i];
4512
4513         if (player->present)
4514         {
4515           RemovePlayer(player);
4516
4517           /* player disappears */
4518           DrawLevelField(player->jx, player->jy);
4519         }
4520       }
4521     }
4522
4523     PlaySound(SND_GAME_WINNING);
4524   }
4525
4526   if (game_over_delay_1 > 0)
4527   {
4528     game_over_delay_1--;
4529
4530     return;
4531   }
4532
4533   if (time != time_final)
4534   {
4535     int time_to_go = ABS(time_final - time);
4536     int time_count_dir = (time < time_final ? +1 : -1);
4537     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4538
4539     time  += time_count_steps * time_count_dir;
4540     score += time_count_steps * level.score[SC_TIME_BONUS];
4541
4542 #if 1
4543     local_player->LevelSolved_CountingTime = time;
4544     local_player->LevelSolved_CountingScore = score;
4545
4546     game_panel_controls[GAME_PANEL_TIME].value = time;
4547     game_panel_controls[GAME_PANEL_SCORE].value = score;
4548
4549     DisplayGameControlValues();
4550 #else
4551     DrawGameValue_Time(time);
4552     DrawGameValue_Score(score);
4553 #endif
4554
4555     if (time == time_final)
4556       StopSound(SND_GAME_LEVELTIME_BONUS);
4557     else if (setup.sound_loops)
4558       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4559     else
4560       PlaySound(SND_GAME_LEVELTIME_BONUS);
4561
4562     return;
4563   }
4564
4565   local_player->LevelSolved_PanelOff = TRUE;
4566
4567   if (game_over_delay_2 > 0)
4568   {
4569     game_over_delay_2--;
4570
4571     return;
4572   }
4573
4574 #if 1
4575   GameEnd();
4576 #endif
4577 }
4578
4579 void GameEnd()
4580 {
4581   int hi_pos;
4582   boolean raise_level = FALSE;
4583
4584   local_player->LevelSolved_GameEnd = TRUE;
4585
4586   CloseDoor(DOOR_CLOSE_1);
4587
4588   if (local_player->LevelSolved_SaveTape)
4589   {
4590 #if 0
4591     TapeStop();
4592 #endif
4593
4594 #if 1
4595     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4596 #else
4597     SaveTape(tape.level_nr);            /* ask to save tape */
4598 #endif
4599   }
4600
4601   if (level_editor_test_game)
4602   {
4603     game_status = GAME_MODE_MAIN;
4604
4605 #if 1
4606     DrawAndFadeInMainMenu(REDRAW_FIELD);
4607 #else
4608     DrawMainMenu();
4609 #endif
4610
4611     return;
4612   }
4613
4614   if (!local_player->LevelSolved_SaveScore)
4615   {
4616 #if 1
4617     FadeOut(REDRAW_FIELD);
4618 #endif
4619
4620     game_status = GAME_MODE_MAIN;
4621
4622     DrawAndFadeInMainMenu(REDRAW_FIELD);
4623
4624     return;
4625   }
4626
4627   if (level_nr == leveldir_current->handicap_level)
4628   {
4629     leveldir_current->handicap_level++;
4630     SaveLevelSetup_SeriesInfo();
4631   }
4632
4633   if (level_nr < leveldir_current->last_level)
4634     raise_level = TRUE;                 /* advance to next level */
4635
4636   if ((hi_pos = NewHiScore()) >= 0) 
4637   {
4638     game_status = GAME_MODE_SCORES;
4639
4640     DrawHallOfFame(hi_pos);
4641
4642     if (raise_level)
4643     {
4644       level_nr++;
4645       TapeErase();
4646     }
4647   }
4648   else
4649   {
4650 #if 1
4651     FadeOut(REDRAW_FIELD);
4652 #endif
4653
4654     game_status = GAME_MODE_MAIN;
4655
4656     if (raise_level)
4657     {
4658       level_nr++;
4659       TapeErase();
4660     }
4661
4662     DrawAndFadeInMainMenu(REDRAW_FIELD);
4663   }
4664 }
4665
4666 int NewHiScore()
4667 {
4668   int k, l;
4669   int position = -1;
4670
4671   LoadScore(level_nr);
4672
4673   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4674       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4675     return -1;
4676
4677   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4678   {
4679     if (local_player->score_final > highscore[k].Score)
4680     {
4681       /* player has made it to the hall of fame */
4682
4683       if (k < MAX_SCORE_ENTRIES - 1)
4684       {
4685         int m = MAX_SCORE_ENTRIES - 1;
4686
4687 #ifdef ONE_PER_NAME
4688         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4689           if (strEqual(setup.player_name, highscore[l].Name))
4690             m = l;
4691         if (m == k)     /* player's new highscore overwrites his old one */
4692           goto put_into_list;
4693 #endif
4694
4695         for (l = m; l > k; l--)
4696         {
4697           strcpy(highscore[l].Name, highscore[l - 1].Name);
4698           highscore[l].Score = highscore[l - 1].Score;
4699         }
4700       }
4701
4702 #ifdef ONE_PER_NAME
4703       put_into_list:
4704 #endif
4705       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4706       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4707       highscore[k].Score = local_player->score_final; 
4708       position = k;
4709       break;
4710     }
4711
4712 #ifdef ONE_PER_NAME
4713     else if (!strncmp(setup.player_name, highscore[k].Name,
4714                       MAX_PLAYER_NAME_LEN))
4715       break;    /* player already there with a higher score */
4716 #endif
4717
4718   }
4719
4720   if (position >= 0) 
4721     SaveScore(level_nr);
4722
4723   return position;
4724 }
4725
4726 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4727 {
4728   int element = Feld[x][y];
4729   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4730   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4731   int horiz_move = (dx != 0);
4732   int sign = (horiz_move ? dx : dy);
4733   int step = sign * element_info[element].move_stepsize;
4734
4735   /* special values for move stepsize for spring and things on conveyor belt */
4736   if (horiz_move)
4737   {
4738     if (CAN_FALL(element) &&
4739         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4740       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4741     else if (element == EL_SPRING)
4742       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4743   }
4744
4745   return step;
4746 }
4747
4748 inline static int getElementMoveStepsize(int x, int y)
4749 {
4750   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4751 }
4752
4753 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4754 {
4755   if (player->GfxAction != action || player->GfxDir != dir)
4756   {
4757 #if 0
4758     printf("Player frame reset! (%d => %d, %d => %d)\n",
4759            player->GfxAction, action, player->GfxDir, dir);
4760 #endif
4761
4762     player->GfxAction = action;
4763     player->GfxDir = dir;
4764     player->Frame = 0;
4765     player->StepFrame = 0;
4766   }
4767 }
4768
4769 #if USE_GFX_RESET_GFX_ANIMATION
4770 static void ResetGfxFrame(int x, int y, boolean redraw)
4771 {
4772   int element = Feld[x][y];
4773   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4774   int last_gfx_frame = GfxFrame[x][y];
4775
4776   if (graphic_info[graphic].anim_global_sync)
4777     GfxFrame[x][y] = FrameCounter;
4778   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4779     GfxFrame[x][y] = CustomValue[x][y];
4780   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4781     GfxFrame[x][y] = element_info[element].collect_score;
4782   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4783     GfxFrame[x][y] = ChangeDelay[x][y];
4784
4785   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4786     DrawLevelGraphicAnimation(x, y, graphic);
4787 }
4788 #endif
4789
4790 static void ResetGfxAnimation(int x, int y)
4791 {
4792   GfxAction[x][y] = ACTION_DEFAULT;
4793   GfxDir[x][y] = MovDir[x][y];
4794   GfxFrame[x][y] = 0;
4795
4796 #if USE_GFX_RESET_GFX_ANIMATION
4797   ResetGfxFrame(x, y, FALSE);
4798 #endif
4799 }
4800
4801 static void ResetRandomAnimationValue(int x, int y)
4802 {
4803   GfxRandom[x][y] = INIT_GFX_RANDOM();
4804 }
4805
4806 void InitMovingField(int x, int y, int direction)
4807 {
4808   int element = Feld[x][y];
4809   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4810   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4811   int newx = x + dx;
4812   int newy = y + dy;
4813   boolean is_moving_before, is_moving_after;
4814 #if 0
4815   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
4816 #endif
4817
4818   /* check if element was/is moving or being moved before/after mode change */
4819 #if 1
4820 #if 1
4821   is_moving_before = (WasJustMoving[x][y] != 0);
4822 #else
4823   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
4824   is_moving_before = WasJustMoving[x][y];
4825 #endif
4826 #else
4827   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
4828 #endif
4829   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4830
4831   /* reset animation only for moving elements which change direction of moving
4832      or which just started or stopped moving
4833      (else CEs with property "can move" / "not moving" are reset each frame) */
4834 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4835 #if 1
4836   if (is_moving_before != is_moving_after ||
4837       direction != MovDir[x][y])
4838     ResetGfxAnimation(x, y);
4839 #else
4840   if ((is_moving_before || is_moving_after) && !continues_moving)
4841     ResetGfxAnimation(x, y);
4842 #endif
4843 #else
4844   if (!continues_moving)
4845     ResetGfxAnimation(x, y);
4846 #endif
4847
4848   MovDir[x][y] = direction;
4849   GfxDir[x][y] = direction;
4850
4851 #if USE_GFX_RESET_ONLY_WHEN_MOVING
4852   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4853                      direction == MV_DOWN && CAN_FALL(element) ?
4854                      ACTION_FALLING : ACTION_MOVING);
4855 #else
4856   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
4857                      ACTION_FALLING : ACTION_MOVING);
4858 #endif
4859
4860   /* this is needed for CEs with property "can move" / "not moving" */
4861
4862   if (is_moving_after)
4863   {
4864     if (Feld[newx][newy] == EL_EMPTY)
4865       Feld[newx][newy] = EL_BLOCKED;
4866
4867     MovDir[newx][newy] = MovDir[x][y];
4868
4869 #if USE_NEW_CUSTOM_VALUE
4870     CustomValue[newx][newy] = CustomValue[x][y];
4871 #endif
4872
4873     GfxFrame[newx][newy] = GfxFrame[x][y];
4874     GfxRandom[newx][newy] = GfxRandom[x][y];
4875     GfxAction[newx][newy] = GfxAction[x][y];
4876     GfxDir[newx][newy] = GfxDir[x][y];
4877   }
4878 }
4879
4880 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4881 {
4882   int direction = MovDir[x][y];
4883   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4884   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4885
4886   *goes_to_x = newx;
4887   *goes_to_y = newy;
4888 }
4889
4890 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4891 {
4892   int oldx = x, oldy = y;
4893   int direction = MovDir[x][y];
4894
4895   if (direction == MV_LEFT)
4896     oldx++;
4897   else if (direction == MV_RIGHT)
4898     oldx--;
4899   else if (direction == MV_UP)
4900     oldy++;
4901   else if (direction == MV_DOWN)
4902     oldy--;
4903
4904   *comes_from_x = oldx;
4905   *comes_from_y = oldy;
4906 }
4907
4908 int MovingOrBlocked2Element(int x, int y)
4909 {
4910   int element = Feld[x][y];
4911
4912   if (element == EL_BLOCKED)
4913   {
4914     int oldx, oldy;
4915
4916     Blocked2Moving(x, y, &oldx, &oldy);
4917     return Feld[oldx][oldy];
4918   }
4919   else
4920     return element;
4921 }
4922
4923 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4924 {
4925   /* like MovingOrBlocked2Element(), but if element is moving
4926      and (x,y) is the field the moving element is just leaving,
4927      return EL_BLOCKED instead of the element value */
4928   int element = Feld[x][y];
4929
4930   if (IS_MOVING(x, y))
4931   {
4932     if (element == EL_BLOCKED)
4933     {
4934       int oldx, oldy;
4935
4936       Blocked2Moving(x, y, &oldx, &oldy);
4937       return Feld[oldx][oldy];
4938     }
4939     else
4940       return EL_BLOCKED;
4941   }
4942   else
4943     return element;
4944 }
4945
4946 static void RemoveField(int x, int y)
4947 {
4948   Feld[x][y] = EL_EMPTY;
4949
4950   MovPos[x][y] = 0;
4951   MovDir[x][y] = 0;
4952   MovDelay[x][y] = 0;
4953
4954 #if USE_NEW_CUSTOM_VALUE
4955   CustomValue[x][y] = 0;
4956 #endif
4957
4958   AmoebaNr[x][y] = 0;
4959   ChangeDelay[x][y] = 0;
4960   ChangePage[x][y] = -1;
4961   Pushed[x][y] = FALSE;
4962
4963 #if 0
4964   ExplodeField[x][y] = EX_TYPE_NONE;
4965 #endif
4966
4967   GfxElement[x][y] = EL_UNDEFINED;
4968   GfxAction[x][y] = ACTION_DEFAULT;
4969   GfxDir[x][y] = MV_NONE;
4970 }
4971
4972 void RemoveMovingField(int x, int y)
4973 {
4974   int oldx = x, oldy = y, newx = x, newy = y;
4975   int element = Feld[x][y];
4976   int next_element = EL_UNDEFINED;
4977
4978   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4979     return;
4980
4981   if (IS_MOVING(x, y))
4982   {
4983     Moving2Blocked(x, y, &newx, &newy);
4984
4985     if (Feld[newx][newy] != EL_BLOCKED)
4986     {
4987       /* element is moving, but target field is not free (blocked), but
4988          already occupied by something different (example: acid pool);
4989          in this case, only remove the moving field, but not the target */
4990
4991       RemoveField(oldx, oldy);
4992
4993       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4994
4995       DrawLevelField(oldx, oldy);
4996
4997       return;
4998     }
4999   }
5000   else if (element == EL_BLOCKED)
5001   {
5002     Blocked2Moving(x, y, &oldx, &oldy);
5003     if (!IS_MOVING(oldx, oldy))
5004       return;
5005   }
5006
5007   if (element == EL_BLOCKED &&
5008       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5009        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5010        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5011        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5012        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5013        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5014     next_element = get_next_element(Feld[oldx][oldy]);
5015
5016   RemoveField(oldx, oldy);
5017   RemoveField(newx, newy);
5018
5019   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5020
5021   if (next_element != EL_UNDEFINED)
5022     Feld[oldx][oldy] = next_element;
5023
5024   DrawLevelField(oldx, oldy);
5025   DrawLevelField(newx, newy);
5026 }
5027
5028 void DrawDynamite(int x, int y)
5029 {
5030   int sx = SCREENX(x), sy = SCREENY(y);
5031   int graphic = el2img(Feld[x][y]);
5032   int frame;
5033
5034   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5035     return;
5036
5037   if (IS_WALKABLE_INSIDE(Back[x][y]))
5038     return;
5039
5040   if (Back[x][y])
5041     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5042   else if (Store[x][y])
5043     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5044
5045   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5046
5047   if (Back[x][y] || Store[x][y])
5048     DrawGraphicThruMask(sx, sy, graphic, frame);
5049   else
5050     DrawGraphic(sx, sy, graphic, frame);
5051 }
5052
5053 void CheckDynamite(int x, int y)
5054 {
5055   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
5056   {
5057     MovDelay[x][y]--;
5058
5059     if (MovDelay[x][y] != 0)
5060     {
5061       DrawDynamite(x, y);
5062       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5063
5064       return;
5065     }
5066   }
5067
5068   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5069
5070   Bang(x, y);
5071 }
5072
5073 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5074 {
5075   boolean num_checked_players = 0;
5076   int i;
5077
5078   for (i = 0; i < MAX_PLAYERS; i++)
5079   {
5080     if (stored_player[i].active)
5081     {
5082       int sx = stored_player[i].jx;
5083       int sy = stored_player[i].jy;
5084
5085       if (num_checked_players == 0)
5086       {
5087         *sx1 = *sx2 = sx;
5088         *sy1 = *sy2 = sy;
5089       }
5090       else
5091       {
5092         *sx1 = MIN(*sx1, sx);
5093         *sy1 = MIN(*sy1, sy);
5094         *sx2 = MAX(*sx2, sx);
5095         *sy2 = MAX(*sy2, sy);
5096       }
5097
5098       num_checked_players++;
5099     }
5100   }
5101 }
5102
5103 static boolean checkIfAllPlayersFitToScreen_RND()
5104 {
5105   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5106
5107   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5108
5109   return (sx2 - sx1 < SCR_FIELDX &&
5110           sy2 - sy1 < SCR_FIELDY);
5111 }
5112
5113 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5114 {
5115   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5116
5117   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5118
5119   *sx = (sx1 + sx2) / 2;
5120   *sy = (sy1 + sy2) / 2;
5121 }
5122
5123 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5124                         boolean center_screen, boolean quick_relocation)
5125 {
5126   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5127   boolean no_delay = (tape.warp_forward);
5128   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5129   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5130
5131   if (quick_relocation)
5132   {
5133     int offset = game.scroll_delay_value;
5134
5135     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5136     {
5137       if (!level.shifted_relocation || center_screen)
5138       {
5139         /* quick relocation (without scrolling), with centering of screen */
5140
5141         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5142                     x > SBX_Right + MIDPOSX ? SBX_Right :
5143                     x - MIDPOSX);
5144
5145         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5146                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5147                     y - MIDPOSY);
5148       }
5149       else
5150       {
5151         /* quick relocation (without scrolling), but do not center screen */
5152
5153         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5154                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
5155                                old_x - MIDPOSX);
5156
5157         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5158                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5159                                old_y - MIDPOSY);
5160
5161         int offset_x = x + (scroll_x - center_scroll_x);
5162         int offset_y = y + (scroll_y - center_scroll_y);
5163
5164         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5165                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5166                     offset_x - MIDPOSX);
5167
5168         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5169                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5170                     offset_y - MIDPOSY);
5171       }
5172     }
5173     else
5174     {
5175       /* quick relocation (without scrolling), inside visible screen area */
5176
5177       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5178           (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5179         scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5180
5181       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5182           (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5183         scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5184
5185       /* don't scroll over playfield boundaries */
5186       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5187         scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5188
5189       /* don't scroll over playfield boundaries */
5190       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5191         scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5192     }
5193
5194     RedrawPlayfield(TRUE, 0,0,0,0);
5195   }
5196   else
5197   {
5198 #if 1
5199     int scroll_xx, scroll_yy;
5200
5201     if (!level.shifted_relocation || center_screen)
5202     {
5203       /* visible relocation (with scrolling), with centering of screen */
5204
5205       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5206                    x > SBX_Right + MIDPOSX ? SBX_Right :
5207                    x - MIDPOSX);
5208
5209       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5210                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5211                    y - MIDPOSY);
5212     }
5213     else
5214     {
5215       /* visible relocation (with scrolling), but do not center screen */
5216
5217       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5218                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5219                              old_x - MIDPOSX);
5220
5221       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5222                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5223                              old_y - MIDPOSY);
5224
5225       int offset_x = x + (scroll_x - center_scroll_x);
5226       int offset_y = y + (scroll_y - center_scroll_y);
5227
5228       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5229                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5230                    offset_x - MIDPOSX);
5231
5232       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5233                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5234                    offset_y - MIDPOSY);
5235     }
5236
5237 #else
5238
5239     /* visible relocation (with scrolling), with centering of screen */
5240
5241     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5242                      x > SBX_Right + MIDPOSX ? SBX_Right :
5243                      x - MIDPOSX);
5244
5245     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5246                      y > SBY_Lower + MIDPOSY ? SBY_Lower :
5247                      y - MIDPOSY);
5248 #endif
5249
5250     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5251
5252     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5253     {
5254       int dx = 0, dy = 0;
5255       int fx = FX, fy = FY;
5256
5257       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5258       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5259
5260       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5261         break;
5262
5263       scroll_x -= dx;
5264       scroll_y -= dy;
5265
5266       fx += dx * TILEX / 2;
5267       fy += dy * TILEY / 2;
5268
5269       ScrollLevel(dx, dy);
5270       DrawAllPlayers();
5271
5272       /* scroll in two steps of half tile size to make things smoother */
5273       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5274       FlushDisplay();
5275       Delay(wait_delay_value);
5276
5277       /* scroll second step to align at full tile size */
5278       BackToFront();
5279       Delay(wait_delay_value);
5280     }
5281
5282     DrawAllPlayers();
5283     BackToFront();
5284     Delay(wait_delay_value);
5285   }
5286 }
5287
5288 void RelocatePlayer(int jx, int jy, int el_player_raw)
5289 {
5290   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5291   int player_nr = GET_PLAYER_NR(el_player);
5292   struct PlayerInfo *player = &stored_player[player_nr];
5293   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5294   boolean no_delay = (tape.warp_forward);
5295   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5296   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5297   int old_jx = player->jx;
5298   int old_jy = player->jy;
5299   int old_element = Feld[old_jx][old_jy];
5300   int element = Feld[jx][jy];
5301   boolean player_relocated = (old_jx != jx || old_jy != jy);
5302
5303   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5304   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5305   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5306   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5307   int leave_side_horiz = move_dir_horiz;
5308   int leave_side_vert  = move_dir_vert;
5309   int enter_side = enter_side_horiz | enter_side_vert;
5310   int leave_side = leave_side_horiz | leave_side_vert;
5311
5312   if (player->GameOver)         /* do not reanimate dead player */
5313     return;
5314
5315   if (!player_relocated)        /* no need to relocate the player */
5316     return;
5317
5318   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5319   {
5320     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5321     DrawLevelField(jx, jy);
5322   }
5323
5324   if (player->present)
5325   {
5326     while (player->MovPos)
5327     {
5328       ScrollPlayer(player, SCROLL_GO_ON);
5329       ScrollScreen(NULL, SCROLL_GO_ON);
5330
5331       AdvanceFrameAndPlayerCounters(player->index_nr);
5332
5333       DrawPlayer(player);
5334
5335       BackToFront();
5336       Delay(wait_delay_value);
5337     }
5338
5339     DrawPlayer(player);         /* needed here only to cleanup last field */
5340     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5341
5342     player->is_moving = FALSE;
5343   }
5344
5345   if (IS_CUSTOM_ELEMENT(old_element))
5346     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5347                                CE_LEFT_BY_PLAYER,
5348                                player->index_bit, leave_side);
5349
5350   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5351                                       CE_PLAYER_LEAVES_X,
5352                                       player->index_bit, leave_side);
5353
5354   Feld[jx][jy] = el_player;
5355   InitPlayerField(jx, jy, el_player, TRUE);
5356
5357   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5358   {
5359     Feld[jx][jy] = element;
5360     InitField(jx, jy, FALSE);
5361   }
5362
5363   /* only visually relocate centered player */
5364   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5365                      FALSE, level.instant_relocation);
5366
5367   TestIfPlayerTouchesBadThing(jx, jy);
5368   TestIfPlayerTouchesCustomElement(jx, jy);
5369
5370   if (IS_CUSTOM_ELEMENT(element))
5371     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5372                                player->index_bit, enter_side);
5373
5374   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5375                                       player->index_bit, enter_side);
5376 }
5377
5378 void Explode(int ex, int ey, int phase, int mode)
5379 {
5380   int x, y;
5381   int last_phase;
5382   int border_element;
5383
5384   /* !!! eliminate this variable !!! */
5385   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5386
5387   if (game.explosions_delayed)
5388   {
5389     ExplodeField[ex][ey] = mode;
5390     return;
5391   }
5392
5393   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5394   {
5395     int center_element = Feld[ex][ey];
5396     int artwork_element, explosion_element;     /* set these values later */
5397
5398 #if 0
5399     /* --- This is only really needed (and now handled) in "Impact()". --- */
5400     /* do not explode moving elements that left the explode field in time */
5401     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5402         center_element == EL_EMPTY &&
5403         (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5404       return;
5405 #endif
5406
5407 #if 0
5408     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5409     if (mode == EX_TYPE_NORMAL ||
5410         mode == EX_TYPE_CENTER ||
5411         mode == EX_TYPE_CROSS)
5412       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5413 #endif
5414
5415     /* remove things displayed in background while burning dynamite */
5416     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5417       Back[ex][ey] = 0;
5418
5419     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5420     {
5421       /* put moving element to center field (and let it explode there) */
5422       center_element = MovingOrBlocked2Element(ex, ey);
5423       RemoveMovingField(ex, ey);
5424       Feld[ex][ey] = center_element;
5425     }
5426
5427     /* now "center_element" is finally determined -- set related values now */
5428     artwork_element = center_element;           /* for custom player artwork */
5429     explosion_element = center_element;         /* for custom player artwork */
5430
5431     if (IS_PLAYER(ex, ey))
5432     {
5433       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5434
5435       artwork_element = stored_player[player_nr].artwork_element;
5436
5437       if (level.use_explosion_element[player_nr])
5438       {
5439         explosion_element = level.explosion_element[player_nr];
5440         artwork_element = explosion_element;
5441       }
5442     }
5443
5444 #if 1
5445     if (mode == EX_TYPE_NORMAL ||
5446         mode == EX_TYPE_CENTER ||
5447         mode == EX_TYPE_CROSS)
5448       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5449 #endif
5450
5451     last_phase = element_info[explosion_element].explosion_delay + 1;
5452
5453     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5454     {
5455       int xx = x - ex + 1;
5456       int yy = y - ey + 1;
5457       int element;
5458
5459       if (!IN_LEV_FIELD(x, y) ||
5460           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5461           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5462         continue;
5463
5464       element = Feld[x][y];
5465
5466       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5467       {
5468         element = MovingOrBlocked2Element(x, y);
5469
5470         if (!IS_EXPLOSION_PROOF(element))
5471           RemoveMovingField(x, y);
5472       }
5473
5474       /* indestructible elements can only explode in center (but not flames) */
5475       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5476                                            mode == EX_TYPE_BORDER)) ||
5477           element == EL_FLAMES)
5478         continue;
5479
5480       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5481          behaviour, for example when touching a yamyam that explodes to rocks
5482          with active deadly shield, a rock is created under the player !!! */
5483       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5484 #if 0
5485       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5486           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5487            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5488 #else
5489       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5490 #endif
5491       {
5492         if (IS_ACTIVE_BOMB(element))
5493         {
5494           /* re-activate things under the bomb like gate or penguin */
5495           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5496           Back[x][y] = 0;
5497         }
5498
5499         continue;
5500       }
5501
5502       /* save walkable background elements while explosion on same tile */
5503       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5504           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5505         Back[x][y] = element;
5506
5507       /* ignite explodable elements reached by other explosion */
5508       if (element == EL_EXPLOSION)
5509         element = Store2[x][y];
5510
5511       if (AmoebaNr[x][y] &&
5512           (element == EL_AMOEBA_FULL ||
5513            element == EL_BD_AMOEBA ||
5514            element == EL_AMOEBA_GROWING))
5515       {
5516         AmoebaCnt[AmoebaNr[x][y]]--;
5517         AmoebaCnt2[AmoebaNr[x][y]]--;
5518       }
5519
5520       RemoveField(x, y);
5521
5522       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5523       {
5524         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5525
5526         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5527
5528         if (PLAYERINFO(ex, ey)->use_murphy)
5529           Store[x][y] = EL_EMPTY;
5530       }
5531
5532       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5533          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5534       else if (ELEM_IS_PLAYER(center_element))
5535         Store[x][y] = EL_EMPTY;
5536       else if (center_element == EL_YAMYAM)
5537         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5538       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5539         Store[x][y] = element_info[center_element].content.e[xx][yy];
5540 #if 1
5541       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5542          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5543          otherwise) -- FIX THIS !!! */
5544       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5545         Store[x][y] = element_info[element].content.e[1][1];
5546 #else
5547       else if (!CAN_EXPLODE(element))
5548         Store[x][y] = element_info[element].content.e[1][1];
5549 #endif
5550       else
5551         Store[x][y] = EL_EMPTY;
5552
5553       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5554           center_element == EL_AMOEBA_TO_DIAMOND)
5555         Store2[x][y] = element;
5556
5557       Feld[x][y] = EL_EXPLOSION;
5558       GfxElement[x][y] = artwork_element;
5559
5560       ExplodePhase[x][y] = 1;
5561       ExplodeDelay[x][y] = last_phase;
5562
5563       Stop[x][y] = TRUE;
5564     }
5565
5566     if (center_element == EL_YAMYAM)
5567       game.yamyam_content_nr =
5568         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5569
5570     return;
5571   }
5572
5573   if (Stop[ex][ey])
5574     return;
5575
5576   x = ex;
5577   y = ey;
5578
5579   if (phase == 1)
5580     GfxFrame[x][y] = 0;         /* restart explosion animation */
5581
5582   last_phase = ExplodeDelay[x][y];
5583
5584   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5585
5586 #ifdef DEBUG
5587
5588   /* activate this even in non-DEBUG version until cause for crash in
5589      getGraphicAnimationFrame() (see below) is found and eliminated */
5590
5591 #endif
5592 #if 1
5593
5594 #if 1
5595   /* this can happen if the player leaves an explosion just in time */
5596   if (GfxElement[x][y] == EL_UNDEFINED)
5597     GfxElement[x][y] = EL_EMPTY;
5598 #else
5599   if (GfxElement[x][y] == EL_UNDEFINED)
5600   {
5601     printf("\n\n");
5602     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5603     printf("Explode(): This should never happen!\n");
5604     printf("\n\n");
5605
5606     GfxElement[x][y] = EL_EMPTY;
5607   }
5608 #endif
5609
5610 #endif
5611
5612   border_element = Store2[x][y];
5613   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5614     border_element = StorePlayer[x][y];
5615
5616   if (phase == element_info[border_element].ignition_delay ||
5617       phase == last_phase)
5618   {
5619     boolean border_explosion = FALSE;
5620
5621     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5622         !PLAYER_EXPLOSION_PROTECTED(x, y))
5623     {
5624       KillPlayerUnlessExplosionProtected(x, y);
5625       border_explosion = TRUE;
5626     }
5627     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5628     {
5629       Feld[x][y] = Store2[x][y];
5630       Store2[x][y] = 0;
5631       Bang(x, y);
5632       border_explosion = TRUE;
5633     }
5634     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5635     {
5636       AmoebeUmwandeln(x, y);
5637       Store2[x][y] = 0;
5638       border_explosion = TRUE;
5639     }
5640
5641     /* if an element just explodes due to another explosion (chain-reaction),
5642        do not immediately end the new explosion when it was the last frame of
5643        the explosion (as it would be done in the following "if"-statement!) */
5644     if (border_explosion && phase == last_phase)
5645       return;
5646   }
5647
5648   if (phase == last_phase)
5649   {
5650     int element;
5651
5652     element = Feld[x][y] = Store[x][y];
5653     Store[x][y] = Store2[x][y] = 0;
5654     GfxElement[x][y] = EL_UNDEFINED;
5655
5656     /* player can escape from explosions and might therefore be still alive */
5657     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5658         element <= EL_PLAYER_IS_EXPLODING_4)
5659     {
5660       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5661       int explosion_element = EL_PLAYER_1 + player_nr;
5662       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5663       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5664
5665       if (level.use_explosion_element[player_nr])
5666         explosion_element = level.explosion_element[player_nr];
5667
5668       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5669                     element_info[explosion_element].content.e[xx][yy]);
5670     }
5671
5672     /* restore probably existing indestructible background element */
5673     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5674       element = Feld[x][y] = Back[x][y];
5675     Back[x][y] = 0;
5676
5677     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5678     GfxDir[x][y] = MV_NONE;
5679     ChangeDelay[x][y] = 0;
5680     ChangePage[x][y] = -1;
5681
5682 #if USE_NEW_CUSTOM_VALUE
5683     CustomValue[x][y] = 0;
5684 #endif
5685
5686     InitField_WithBug2(x, y, FALSE);
5687
5688     DrawLevelField(x, y);
5689
5690     TestIfElementTouchesCustomElement(x, y);
5691
5692     if (GFX_CRUMBLED(element))
5693       DrawLevelFieldCrumbledSandNeighbours(x, y);
5694
5695     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5696       StorePlayer[x][y] = 0;
5697
5698     if (ELEM_IS_PLAYER(element))
5699       RelocatePlayer(x, y, element);
5700   }
5701   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5702   {
5703     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5704     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5705
5706     if (phase == delay)
5707       DrawLevelFieldCrumbledSand(x, y);
5708
5709     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5710     {
5711       DrawLevelElement(x, y, Back[x][y]);
5712       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5713     }
5714     else if (IS_WALKABLE_UNDER(Back[x][y]))
5715     {
5716       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5717       DrawLevelElementThruMask(x, y, Back[x][y]);
5718     }
5719     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5720       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5721   }
5722 }
5723
5724 void DynaExplode(int ex, int ey)
5725 {
5726   int i, j;
5727   int dynabomb_element = Feld[ex][ey];
5728   int dynabomb_size = 1;
5729   boolean dynabomb_xl = FALSE;
5730   struct PlayerInfo *player;
5731   static int xy[4][2] =
5732   {
5733     { 0, -1 },
5734     { -1, 0 },
5735     { +1, 0 },
5736     { 0, +1 }
5737   };
5738
5739   if (IS_ACTIVE_BOMB(dynabomb_element))
5740   {
5741     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5742     dynabomb_size = player->dynabomb_size;
5743     dynabomb_xl = player->dynabomb_xl;
5744     player->dynabombs_left++;
5745   }
5746
5747   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5748
5749   for (i = 0; i < NUM_DIRECTIONS; i++)
5750   {
5751     for (j = 1; j <= dynabomb_size; j++)
5752     {
5753       int x = ex + j * xy[i][0];
5754       int y = ey + j * xy[i][1];
5755       int element;
5756
5757       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5758         break;
5759
5760       element = Feld[x][y];
5761
5762       /* do not restart explosions of fields with active bombs */
5763       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5764         continue;
5765
5766       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5767
5768       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5769           !IS_DIGGABLE(element) && !dynabomb_xl)
5770         break;
5771     }
5772   }
5773 }
5774
5775 void Bang(int x, int y)
5776 {
5777   int element = MovingOrBlocked2Element(x, y);
5778   int explosion_type = EX_TYPE_NORMAL;
5779
5780   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5781   {
5782     struct PlayerInfo *player = PLAYERINFO(x, y);
5783
5784     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
5785                             player->element_nr);
5786
5787     if (level.use_explosion_element[player->index_nr])
5788     {
5789       int explosion_element = level.explosion_element[player->index_nr];
5790
5791       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5792         explosion_type = EX_TYPE_CROSS;
5793       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5794         explosion_type = EX_TYPE_CENTER;
5795     }
5796   }
5797
5798   switch (element)
5799   {
5800     case EL_BUG:
5801     case EL_SPACESHIP:
5802     case EL_BD_BUTTERFLY:
5803     case EL_BD_FIREFLY:
5804     case EL_YAMYAM:
5805     case EL_DARK_YAMYAM:
5806     case EL_ROBOT:
5807     case EL_PACMAN:
5808     case EL_MOLE:
5809       RaiseScoreElement(element);
5810       break;
5811
5812     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5813     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5814     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5815     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5816     case EL_DYNABOMB_INCREASE_NUMBER:
5817     case EL_DYNABOMB_INCREASE_SIZE:
5818     case EL_DYNABOMB_INCREASE_POWER:
5819       explosion_type = EX_TYPE_DYNA;
5820       break;
5821
5822     case EL_DC_LANDMINE:
5823 #if 0
5824     case EL_EM_EXIT_OPEN:
5825     case EL_EM_STEEL_EXIT_OPEN:
5826 #endif
5827       explosion_type = EX_TYPE_CENTER;
5828       break;
5829
5830     case EL_PENGUIN:
5831     case EL_LAMP:
5832     case EL_LAMP_ACTIVE:
5833     case EL_AMOEBA_TO_DIAMOND:
5834       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5835         explosion_type = EX_TYPE_CENTER;
5836       break;
5837
5838     default:
5839       if (element_info[element].explosion_type == EXPLODES_CROSS)
5840         explosion_type = EX_TYPE_CROSS;
5841       else if (element_info[element].explosion_type == EXPLODES_1X1)
5842         explosion_type = EX_TYPE_CENTER;
5843       break;
5844   }
5845
5846   if (explosion_type == EX_TYPE_DYNA)
5847     DynaExplode(x, y);
5848   else
5849     Explode(x, y, EX_PHASE_START, explosion_type);
5850
5851   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5852 }
5853
5854 void SplashAcid(int x, int y)
5855 {
5856   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5857       (!IN_LEV_FIELD(x - 1, y - 2) ||
5858        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5859     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5860
5861   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5862       (!IN_LEV_FIELD(x + 1, y - 2) ||
5863        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5864     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5865
5866   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5867 }
5868
5869 static void InitBeltMovement()
5870 {
5871   static int belt_base_element[4] =
5872   {
5873     EL_CONVEYOR_BELT_1_LEFT,
5874     EL_CONVEYOR_BELT_2_LEFT,
5875     EL_CONVEYOR_BELT_3_LEFT,
5876     EL_CONVEYOR_BELT_4_LEFT
5877   };
5878   static int belt_base_active_element[4] =
5879   {
5880     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5881     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5882     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5883     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5884   };
5885
5886   int x, y, i, j;
5887
5888   /* set frame order for belt animation graphic according to belt direction */
5889   for (i = 0; i < NUM_BELTS; i++)
5890   {
5891     int belt_nr = i;
5892
5893     for (j = 0; j < NUM_BELT_PARTS; j++)
5894     {
5895       int element = belt_base_active_element[belt_nr] + j;
5896       int graphic_1 = el2img(element);
5897       int graphic_2 = el2panelimg(element);
5898
5899       if (game.belt_dir[i] == MV_LEFT)
5900       {
5901         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5902         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5903       }
5904       else
5905       {
5906         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5907         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5908       }
5909     }
5910   }
5911
5912   SCAN_PLAYFIELD(x, y)
5913   {
5914     int element = Feld[x][y];
5915
5916     for (i = 0; i < NUM_BELTS; i++)
5917     {
5918       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5919       {
5920         int e_belt_nr = getBeltNrFromBeltElement(element);
5921         int belt_nr = i;
5922
5923         if (e_belt_nr == belt_nr)
5924         {
5925           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5926
5927           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5928         }
5929       }
5930     }
5931   }
5932 }
5933
5934 static void ToggleBeltSwitch(int x, int y)
5935 {
5936   static int belt_base_element[4] =
5937   {
5938     EL_CONVEYOR_BELT_1_LEFT,
5939     EL_CONVEYOR_BELT_2_LEFT,
5940     EL_CONVEYOR_BELT_3_LEFT,
5941     EL_CONVEYOR_BELT_4_LEFT
5942   };
5943   static int belt_base_active_element[4] =
5944   {
5945     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5946     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5947     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5948     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5949   };
5950   static int belt_base_switch_element[4] =
5951   {
5952     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5953     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5954     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5955     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5956   };
5957   static int belt_move_dir[4] =
5958   {
5959     MV_LEFT,
5960     MV_NONE,
5961     MV_RIGHT,
5962     MV_NONE,
5963   };
5964
5965   int element = Feld[x][y];
5966   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5967   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5968   int belt_dir = belt_move_dir[belt_dir_nr];
5969   int xx, yy, i;
5970
5971   if (!IS_BELT_SWITCH(element))
5972     return;
5973
5974   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5975   game.belt_dir[belt_nr] = belt_dir;
5976
5977   if (belt_dir_nr == 3)
5978     belt_dir_nr = 1;
5979
5980   /* set frame order for belt animation graphic according to belt direction */
5981   for (i = 0; i < NUM_BELT_PARTS; i++)
5982   {
5983     int element = belt_base_active_element[belt_nr] + i;
5984     int graphic_1 = el2img(element);
5985     int graphic_2 = el2panelimg(element);
5986
5987     if (belt_dir == MV_LEFT)
5988     {
5989       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5990       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5991     }
5992     else
5993     {
5994       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5995       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5996     }
5997   }
5998
5999   SCAN_PLAYFIELD(xx, yy)
6000   {
6001     int element = Feld[xx][yy];
6002
6003     if (IS_BELT_SWITCH(element))
6004     {
6005       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6006
6007       if (e_belt_nr == belt_nr)
6008       {
6009         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6010         DrawLevelField(xx, yy);
6011       }
6012     }
6013     else if (IS_BELT(element) && belt_dir != MV_NONE)
6014     {
6015       int e_belt_nr = getBeltNrFromBeltElement(element);
6016
6017       if (e_belt_nr == belt_nr)
6018       {
6019         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6020
6021         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6022         DrawLevelField(xx, yy);
6023       }
6024     }
6025     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6026     {
6027       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6028
6029       if (e_belt_nr == belt_nr)
6030       {
6031         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6032
6033         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6034         DrawLevelField(xx, yy);
6035       }
6036     }
6037   }
6038 }
6039
6040 static void ToggleSwitchgateSwitch(int x, int y)
6041 {
6042   int xx, yy;
6043
6044   game.switchgate_pos = !game.switchgate_pos;
6045
6046   SCAN_PLAYFIELD(xx, yy)
6047   {
6048     int element = Feld[xx][yy];
6049
6050 #if !USE_BOTH_SWITCHGATE_SWITCHES
6051     if (element == EL_SWITCHGATE_SWITCH_UP ||
6052         element == EL_SWITCHGATE_SWITCH_DOWN)
6053     {
6054       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6055       DrawLevelField(xx, yy);
6056     }
6057     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6058              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6059     {
6060       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6061       DrawLevelField(xx, yy);
6062     }
6063 #else
6064     if (element == EL_SWITCHGATE_SWITCH_UP)
6065     {
6066       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6067       DrawLevelField(xx, yy);
6068     }
6069     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6070     {
6071       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6072       DrawLevelField(xx, yy);
6073     }
6074     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6075     {
6076       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6077       DrawLevelField(xx, yy);
6078     }
6079     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6080     {
6081       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6082       DrawLevelField(xx, yy);
6083     }
6084 #endif
6085     else if (element == EL_SWITCHGATE_OPEN ||
6086              element == EL_SWITCHGATE_OPENING)
6087     {
6088       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6089
6090       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6091     }
6092     else if (element == EL_SWITCHGATE_CLOSED ||
6093              element == EL_SWITCHGATE_CLOSING)
6094     {
6095       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6096
6097       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6098     }
6099   }
6100 }
6101
6102 static int getInvisibleActiveFromInvisibleElement(int element)
6103 {
6104   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6105           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6106           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6107           element);
6108 }
6109
6110 static int getInvisibleFromInvisibleActiveElement(int element)
6111 {
6112   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6113           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6114           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6115           element);
6116 }
6117
6118 static void RedrawAllLightSwitchesAndInvisibleElements()
6119 {
6120   int x, y;
6121
6122   SCAN_PLAYFIELD(x, y)
6123   {
6124     int element = Feld[x][y];
6125
6126     if (element == EL_LIGHT_SWITCH &&
6127         game.light_time_left > 0)
6128     {
6129       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6130       DrawLevelField(x, y);
6131     }
6132     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6133              game.light_time_left == 0)
6134     {
6135       Feld[x][y] = EL_LIGHT_SWITCH;
6136       DrawLevelField(x, y);
6137     }
6138     else if (element == EL_EMC_DRIPPER &&
6139              game.light_time_left > 0)
6140     {
6141       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6142       DrawLevelField(x, y);
6143     }
6144     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6145              game.light_time_left == 0)
6146     {
6147       Feld[x][y] = EL_EMC_DRIPPER;
6148       DrawLevelField(x, y);
6149     }
6150     else if (element == EL_INVISIBLE_STEELWALL ||
6151              element == EL_INVISIBLE_WALL ||
6152              element == EL_INVISIBLE_SAND)
6153     {
6154       if (game.light_time_left > 0)
6155         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6156
6157       DrawLevelField(x, y);
6158
6159       /* uncrumble neighbour fields, if needed */
6160       if (element == EL_INVISIBLE_SAND)
6161         DrawLevelFieldCrumbledSandNeighbours(x, y);
6162     }
6163     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6164              element == EL_INVISIBLE_WALL_ACTIVE ||
6165              element == EL_INVISIBLE_SAND_ACTIVE)
6166     {
6167       if (game.light_time_left == 0)
6168         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6169
6170       DrawLevelField(x, y);
6171
6172       /* re-crumble neighbour fields, if needed */
6173       if (element == EL_INVISIBLE_SAND)
6174         DrawLevelFieldCrumbledSandNeighbours(x, y);
6175     }
6176   }
6177 }
6178
6179 static void RedrawAllInvisibleElementsForLenses()
6180 {
6181   int x, y;
6182
6183   SCAN_PLAYFIELD(x, y)
6184   {
6185     int element = Feld[x][y];
6186
6187     if (element == EL_EMC_DRIPPER &&
6188         game.lenses_time_left > 0)
6189     {
6190       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6191       DrawLevelField(x, y);
6192     }
6193     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6194              game.lenses_time_left == 0)
6195     {
6196       Feld[x][y] = EL_EMC_DRIPPER;
6197       DrawLevelField(x, y);
6198     }
6199     else if (element == EL_INVISIBLE_STEELWALL ||
6200              element == EL_INVISIBLE_WALL ||
6201              element == EL_INVISIBLE_SAND)
6202     {
6203       if (game.lenses_time_left > 0)
6204         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6205
6206       DrawLevelField(x, y);
6207
6208       /* uncrumble neighbour fields, if needed */
6209       if (element == EL_INVISIBLE_SAND)
6210         DrawLevelFieldCrumbledSandNeighbours(x, y);
6211     }
6212     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6213              element == EL_INVISIBLE_WALL_ACTIVE ||
6214              element == EL_INVISIBLE_SAND_ACTIVE)
6215     {
6216       if (game.lenses_time_left == 0)
6217         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6218
6219       DrawLevelField(x, y);
6220
6221       /* re-crumble neighbour fields, if needed */
6222       if (element == EL_INVISIBLE_SAND)
6223         DrawLevelFieldCrumbledSandNeighbours(x, y);
6224     }
6225   }
6226 }
6227
6228 static void RedrawAllInvisibleElementsForMagnifier()
6229 {
6230   int x, y;
6231
6232   SCAN_PLAYFIELD(x, y)
6233   {
6234     int element = Feld[x][y];
6235
6236     if (element == EL_EMC_FAKE_GRASS &&
6237         game.magnify_time_left > 0)
6238     {
6239       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6240       DrawLevelField(x, y);
6241     }
6242     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6243              game.magnify_time_left == 0)
6244     {
6245       Feld[x][y] = EL_EMC_FAKE_GRASS;
6246       DrawLevelField(x, y);
6247     }
6248     else if (IS_GATE_GRAY(element) &&
6249              game.magnify_time_left > 0)
6250     {
6251       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6252                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6253                     IS_EM_GATE_GRAY(element) ?
6254                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6255                     IS_EMC_GATE_GRAY(element) ?
6256                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6257                     element);
6258       DrawLevelField(x, y);
6259     }
6260     else if (IS_GATE_GRAY_ACTIVE(element) &&
6261              game.magnify_time_left == 0)
6262     {
6263       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6264                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6265                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6266                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6267                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6268                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6269                     element);
6270       DrawLevelField(x, y);
6271     }
6272   }
6273 }
6274
6275 static void ToggleLightSwitch(int x, int y)
6276 {
6277   int element = Feld[x][y];
6278
6279   game.light_time_left =
6280     (element == EL_LIGHT_SWITCH ?
6281      level.time_light * FRAMES_PER_SECOND : 0);
6282
6283   RedrawAllLightSwitchesAndInvisibleElements();
6284 }
6285
6286 static void ActivateTimegateSwitch(int x, int y)
6287 {
6288   int xx, yy;
6289
6290   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6291
6292   SCAN_PLAYFIELD(xx, yy)
6293   {
6294     int element = Feld[xx][yy];
6295
6296     if (element == EL_TIMEGATE_CLOSED ||
6297         element == EL_TIMEGATE_CLOSING)
6298     {
6299       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6300       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6301     }
6302
6303     /*
6304     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6305     {
6306       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6307       DrawLevelField(xx, yy);
6308     }
6309     */
6310
6311   }
6312
6313 #if 1
6314   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6315                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6316 #else
6317   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6318 #endif
6319 }
6320
6321 void Impact(int x, int y)
6322 {
6323   boolean last_line = (y == lev_fieldy - 1);
6324   boolean object_hit = FALSE;
6325   boolean impact = (last_line || object_hit);
6326   int element = Feld[x][y];
6327   int smashed = EL_STEELWALL;
6328
6329   if (!last_line)       /* check if element below was hit */
6330   {
6331     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6332       return;
6333
6334     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6335                                          MovDir[x][y + 1] != MV_DOWN ||
6336                                          MovPos[x][y + 1] <= TILEY / 2));
6337
6338     /* do not smash moving elements that left the smashed field in time */
6339     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6340         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6341       object_hit = FALSE;
6342
6343 #if USE_QUICKSAND_IMPACT_BUGFIX
6344     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6345     {
6346       RemoveMovingField(x, y + 1);
6347       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6348       Feld[x][y + 2] = EL_ROCK;
6349       DrawLevelField(x, y + 2);
6350
6351       object_hit = TRUE;
6352     }
6353
6354     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6355     {
6356       RemoveMovingField(x, y + 1);
6357       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6358       Feld[x][y + 2] = EL_ROCK;
6359       DrawLevelField(x, y + 2);
6360
6361       object_hit = TRUE;
6362     }
6363 #endif
6364
6365     if (object_hit)
6366       smashed = MovingOrBlocked2Element(x, y + 1);
6367
6368     impact = (last_line || object_hit);
6369   }
6370
6371   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6372   {
6373     SplashAcid(x, y + 1);
6374     return;
6375   }
6376
6377   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6378   /* only reset graphic animation if graphic really changes after impact */
6379   if (impact &&
6380       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6381   {
6382     ResetGfxAnimation(x, y);
6383     DrawLevelField(x, y);
6384   }
6385
6386   if (impact && CAN_EXPLODE_IMPACT(element))
6387   {
6388     Bang(x, y);
6389     return;
6390   }
6391   else if (impact && element == EL_PEARL &&
6392            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6393   {
6394     ResetGfxAnimation(x, y);
6395
6396     Feld[x][y] = EL_PEARL_BREAKING;
6397     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6398     return;
6399   }
6400   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6401   {
6402     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6403
6404     return;
6405   }
6406
6407   if (impact && element == EL_AMOEBA_DROP)
6408   {
6409     if (object_hit && IS_PLAYER(x, y + 1))
6410       KillPlayerUnlessEnemyProtected(x, y + 1);
6411     else if (object_hit && smashed == EL_PENGUIN)
6412       Bang(x, y + 1);
6413     else
6414     {
6415       Feld[x][y] = EL_AMOEBA_GROWING;
6416       Store[x][y] = EL_AMOEBA_WET;
6417
6418       ResetRandomAnimationValue(x, y);
6419     }
6420     return;
6421   }
6422
6423   if (object_hit)               /* check which object was hit */
6424   {
6425     if ((CAN_PASS_MAGIC_WALL(element) && 
6426          (smashed == EL_MAGIC_WALL ||
6427           smashed == EL_BD_MAGIC_WALL)) ||
6428         (CAN_PASS_DC_MAGIC_WALL(element) &&
6429          smashed == EL_DC_MAGIC_WALL))
6430     {
6431       int xx, yy;
6432       int activated_magic_wall =
6433         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6434          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6435          EL_DC_MAGIC_WALL_ACTIVE);
6436
6437       /* activate magic wall / mill */
6438       SCAN_PLAYFIELD(xx, yy)
6439       {
6440         if (Feld[xx][yy] == smashed)
6441           Feld[xx][yy] = activated_magic_wall;
6442       }
6443
6444       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6445       game.magic_wall_active = TRUE;
6446
6447       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6448                             SND_MAGIC_WALL_ACTIVATING :
6449                             smashed == EL_BD_MAGIC_WALL ?
6450                             SND_BD_MAGIC_WALL_ACTIVATING :
6451                             SND_DC_MAGIC_WALL_ACTIVATING));
6452     }
6453
6454     if (IS_PLAYER(x, y + 1))
6455     {
6456       if (CAN_SMASH_PLAYER(element))
6457       {
6458         KillPlayerUnlessEnemyProtected(x, y + 1);
6459         return;
6460       }
6461     }
6462     else if (smashed == EL_PENGUIN)
6463     {
6464       if (CAN_SMASH_PLAYER(element))
6465       {
6466         Bang(x, y + 1);
6467         return;
6468       }
6469     }
6470     else if (element == EL_BD_DIAMOND)
6471     {
6472       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6473       {
6474         Bang(x, y + 1);
6475         return;
6476       }
6477     }
6478     else if (((element == EL_SP_INFOTRON ||
6479                element == EL_SP_ZONK) &&
6480               (smashed == EL_SP_SNIKSNAK ||
6481                smashed == EL_SP_ELECTRON ||
6482                smashed == EL_SP_DISK_ORANGE)) ||
6483              (element == EL_SP_INFOTRON &&
6484               smashed == EL_SP_DISK_YELLOW))
6485     {
6486       Bang(x, y + 1);
6487       return;
6488     }
6489     else if (CAN_SMASH_EVERYTHING(element))
6490     {
6491       if (IS_CLASSIC_ENEMY(smashed) ||
6492           CAN_EXPLODE_SMASHED(smashed))
6493       {
6494         Bang(x, y + 1);
6495         return;
6496       }
6497       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6498       {
6499         if (smashed == EL_LAMP ||
6500             smashed == EL_LAMP_ACTIVE)
6501         {
6502           Bang(x, y + 1);
6503           return;
6504         }
6505         else if (smashed == EL_NUT)
6506         {
6507           Feld[x][y + 1] = EL_NUT_BREAKING;
6508           PlayLevelSound(x, y, SND_NUT_BREAKING);
6509           RaiseScoreElement(EL_NUT);
6510           return;
6511         }
6512         else if (smashed == EL_PEARL)
6513         {
6514           ResetGfxAnimation(x, y);
6515
6516           Feld[x][y + 1] = EL_PEARL_BREAKING;
6517           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6518           return;
6519         }
6520         else if (smashed == EL_DIAMOND)
6521         {
6522           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6523           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6524           return;
6525         }
6526         else if (IS_BELT_SWITCH(smashed))
6527         {
6528           ToggleBeltSwitch(x, y + 1);
6529         }
6530         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6531                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6532                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6533                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6534         {
6535           ToggleSwitchgateSwitch(x, y + 1);
6536         }
6537         else if (smashed == EL_LIGHT_SWITCH ||
6538                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6539         {
6540           ToggleLightSwitch(x, y + 1);
6541         }
6542         else
6543         {
6544 #if 0
6545           TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6546 #endif
6547
6548           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6549
6550           CheckElementChangeBySide(x, y + 1, smashed, element,
6551                                    CE_SWITCHED, CH_SIDE_TOP);
6552           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6553                                             CH_SIDE_TOP);
6554         }
6555       }
6556       else
6557       {
6558         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6559       }
6560     }
6561   }
6562
6563   /* play sound of magic wall / mill */
6564   if (!last_line &&
6565       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6566        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6567        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6568   {
6569     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6570       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6571     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6572       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6573     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6574       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6575
6576     return;
6577   }
6578
6579   /* play sound of object that hits the ground */
6580   if (last_line || object_hit)
6581     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6582 }
6583
6584 inline static void TurnRoundExt(int x, int y)
6585 {
6586   static struct
6587   {
6588     int dx, dy;
6589   } move_xy[] =
6590   {
6591     {  0,  0 },
6592     { -1,  0 },
6593     { +1,  0 },
6594     {  0,  0 },
6595     {  0, -1 },
6596     {  0,  0 }, { 0, 0 }, { 0, 0 },
6597     {  0, +1 }
6598   };
6599   static struct
6600   {
6601     int left, right, back;
6602   } turn[] =
6603   {
6604     { 0,        0,              0        },
6605     { MV_DOWN,  MV_UP,          MV_RIGHT },
6606     { MV_UP,    MV_DOWN,        MV_LEFT  },
6607     { 0,        0,              0        },
6608     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6609     { 0,        0,              0        },
6610     { 0,        0,              0        },
6611     { 0,        0,              0        },
6612     { MV_RIGHT, MV_LEFT,        MV_UP    }
6613   };
6614
6615   int element = Feld[x][y];
6616   int move_pattern = element_info[element].move_pattern;
6617
6618   int old_move_dir = MovDir[x][y];
6619   int left_dir  = turn[old_move_dir].left;
6620   int right_dir = turn[old_move_dir].right;
6621   int back_dir  = turn[old_move_dir].back;
6622
6623   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6624   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6625   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6626   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6627
6628   int left_x  = x + left_dx,  left_y  = y + left_dy;
6629   int right_x = x + right_dx, right_y = y + right_dy;
6630   int move_x  = x + move_dx,  move_y  = y + move_dy;
6631
6632   int xx, yy;
6633
6634   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6635   {
6636     TestIfBadThingTouchesOtherBadThing(x, y);
6637
6638     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6639       MovDir[x][y] = right_dir;
6640     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6641       MovDir[x][y] = left_dir;
6642
6643     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6644       MovDelay[x][y] = 9;
6645     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6646       MovDelay[x][y] = 1;
6647   }
6648   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6649   {
6650     TestIfBadThingTouchesOtherBadThing(x, y);
6651
6652     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6653       MovDir[x][y] = left_dir;
6654     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6655       MovDir[x][y] = right_dir;
6656
6657     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6658       MovDelay[x][y] = 9;
6659     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6660       MovDelay[x][y] = 1;
6661   }
6662   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6663   {
6664     TestIfBadThingTouchesOtherBadThing(x, y);
6665
6666     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6667       MovDir[x][y] = left_dir;
6668     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6669       MovDir[x][y] = right_dir;
6670
6671     if (MovDir[x][y] != old_move_dir)
6672       MovDelay[x][y] = 9;
6673   }
6674   else if (element == EL_YAMYAM)
6675   {
6676     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6677     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6678
6679     if (can_turn_left && can_turn_right)
6680       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6681     else if (can_turn_left)
6682       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6683     else if (can_turn_right)
6684       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6685     else
6686       MovDir[x][y] = back_dir;
6687
6688     MovDelay[x][y] = 16 + 16 * RND(3);
6689   }
6690   else if (element == EL_DARK_YAMYAM)
6691   {
6692     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6693                                                          left_x, left_y);
6694     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6695                                                          right_x, right_y);
6696
6697     if (can_turn_left && can_turn_right)
6698       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6699     else if (can_turn_left)
6700       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6701     else if (can_turn_right)
6702       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6703     else
6704       MovDir[x][y] = back_dir;
6705
6706     MovDelay[x][y] = 16 + 16 * RND(3);
6707   }
6708   else if (element == EL_PACMAN)
6709   {
6710     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6711     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6712
6713     if (can_turn_left && can_turn_right)
6714       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6715     else if (can_turn_left)
6716       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6717     else if (can_turn_right)
6718       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6719     else
6720       MovDir[x][y] = back_dir;
6721
6722     MovDelay[x][y] = 6 + RND(40);
6723   }
6724   else if (element == EL_PIG)
6725   {
6726     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6727     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6728     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6729     boolean should_turn_left, should_turn_right, should_move_on;
6730     int rnd_value = 24;
6731     int rnd = RND(rnd_value);
6732
6733     should_turn_left = (can_turn_left &&
6734                         (!can_move_on ||
6735                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6736                                                    y + back_dy + left_dy)));
6737     should_turn_right = (can_turn_right &&
6738                          (!can_move_on ||
6739                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6740                                                     y + back_dy + right_dy)));
6741     should_move_on = (can_move_on &&
6742                       (!can_turn_left ||
6743                        !can_turn_right ||
6744                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6745                                                  y + move_dy + left_dy) ||
6746                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6747                                                  y + move_dy + right_dy)));
6748
6749     if (should_turn_left || should_turn_right || should_move_on)
6750     {
6751       if (should_turn_left && should_turn_right && should_move_on)
6752         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6753                         rnd < 2 * rnd_value / 3 ? right_dir :
6754                         old_move_dir);
6755       else if (should_turn_left && should_turn_right)
6756         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6757       else if (should_turn_left && should_move_on)
6758         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6759       else if (should_turn_right && should_move_on)
6760         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6761       else if (should_turn_left)
6762         MovDir[x][y] = left_dir;
6763       else if (should_turn_right)
6764         MovDir[x][y] = right_dir;
6765       else if (should_move_on)
6766         MovDir[x][y] = old_move_dir;
6767     }
6768     else if (can_move_on && rnd > rnd_value / 8)
6769       MovDir[x][y] = old_move_dir;
6770     else if (can_turn_left && can_turn_right)
6771       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6772     else if (can_turn_left && rnd > rnd_value / 8)
6773       MovDir[x][y] = left_dir;
6774     else if (can_turn_right && rnd > rnd_value/8)
6775       MovDir[x][y] = right_dir;
6776     else
6777       MovDir[x][y] = back_dir;
6778
6779     xx = x + move_xy[MovDir[x][y]].dx;
6780     yy = y + move_xy[MovDir[x][y]].dy;
6781
6782     if (!IN_LEV_FIELD(xx, yy) ||
6783         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6784       MovDir[x][y] = old_move_dir;
6785
6786     MovDelay[x][y] = 0;
6787   }
6788   else if (element == EL_DRAGON)
6789   {
6790     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6791     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6792     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6793     int rnd_value = 24;
6794     int rnd = RND(rnd_value);
6795
6796     if (can_move_on && rnd > rnd_value / 8)
6797       MovDir[x][y] = old_move_dir;
6798     else if (can_turn_left && can_turn_right)
6799       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6800     else if (can_turn_left && rnd > rnd_value / 8)
6801       MovDir[x][y] = left_dir;
6802     else if (can_turn_right && rnd > rnd_value / 8)
6803       MovDir[x][y] = right_dir;
6804     else
6805       MovDir[x][y] = back_dir;
6806
6807     xx = x + move_xy[MovDir[x][y]].dx;
6808     yy = y + move_xy[MovDir[x][y]].dy;
6809
6810     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6811       MovDir[x][y] = old_move_dir;
6812
6813     MovDelay[x][y] = 0;
6814   }
6815   else if (element == EL_MOLE)
6816   {
6817     boolean can_move_on =
6818       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6819                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6820                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6821     if (!can_move_on)
6822     {
6823       boolean can_turn_left =
6824         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6825                               IS_AMOEBOID(Feld[left_x][left_y])));
6826
6827       boolean can_turn_right =
6828         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6829                               IS_AMOEBOID(Feld[right_x][right_y])));
6830
6831       if (can_turn_left && can_turn_right)
6832         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6833       else if (can_turn_left)
6834         MovDir[x][y] = left_dir;
6835       else
6836         MovDir[x][y] = right_dir;
6837     }
6838
6839     if (MovDir[x][y] != old_move_dir)
6840       MovDelay[x][y] = 9;
6841   }
6842   else if (element == EL_BALLOON)
6843   {
6844     MovDir[x][y] = game.wind_direction;
6845     MovDelay[x][y] = 0;
6846   }
6847   else if (element == EL_SPRING)
6848   {
6849 #if USE_NEW_SPRING_BUMPER
6850     if (MovDir[x][y] & MV_HORIZONTAL)
6851     {
6852       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6853           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6854       {
6855         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6856         ResetGfxAnimation(move_x, move_y);
6857         DrawLevelField(move_x, move_y);
6858
6859         MovDir[x][y] = back_dir;
6860       }
6861       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6862                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6863         MovDir[x][y] = MV_NONE;
6864     }
6865 #else
6866     if (MovDir[x][y] & MV_HORIZONTAL &&
6867         (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6868          SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
6869       MovDir[x][y] = MV_NONE;
6870 #endif
6871
6872     MovDelay[x][y] = 0;
6873   }
6874   else if (element == EL_ROBOT ||
6875            element == EL_SATELLITE ||
6876            element == EL_PENGUIN ||
6877            element == EL_EMC_ANDROID)
6878   {
6879     int attr_x = -1, attr_y = -1;
6880
6881     if (AllPlayersGone)
6882     {
6883       attr_x = ExitX;
6884       attr_y = ExitY;
6885     }
6886     else
6887     {
6888       int i;
6889
6890       for (i = 0; i < MAX_PLAYERS; i++)
6891       {
6892         struct PlayerInfo *player = &stored_player[i];
6893         int jx = player->jx, jy = player->jy;
6894
6895         if (!player->active)
6896           continue;
6897
6898         if (attr_x == -1 ||
6899             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6900         {
6901           attr_x = jx;
6902           attr_y = jy;
6903         }
6904       }
6905     }
6906
6907     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6908         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6909          game.engine_version < VERSION_IDENT(3,1,0,0)))
6910     {
6911       attr_x = ZX;
6912       attr_y = ZY;
6913     }
6914
6915     if (element == EL_PENGUIN)
6916     {
6917       int i;
6918       static int xy[4][2] =
6919       {
6920         { 0, -1 },
6921         { -1, 0 },
6922         { +1, 0 },
6923         { 0, +1 }
6924       };
6925
6926       for (i = 0; i < NUM_DIRECTIONS; i++)
6927       {
6928         int ex = x + xy[i][0];
6929         int ey = y + xy[i][1];
6930
6931         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6932                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6933                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6934                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6935         {
6936           attr_x = ex;
6937           attr_y = ey;
6938           break;
6939         }
6940       }
6941     }
6942
6943     MovDir[x][y] = MV_NONE;
6944     if (attr_x < x)
6945       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6946     else if (attr_x > x)
6947       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6948     if (attr_y < y)
6949       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6950     else if (attr_y > y)
6951       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6952
6953     if (element == EL_ROBOT)
6954     {
6955       int newx, newy;
6956
6957       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6958         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6959       Moving2Blocked(x, y, &newx, &newy);
6960
6961       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6962         MovDelay[x][y] = 8 + 8 * !RND(3);
6963       else
6964         MovDelay[x][y] = 16;
6965     }
6966     else if (element == EL_PENGUIN)
6967     {
6968       int newx, newy;
6969
6970       MovDelay[x][y] = 1;
6971
6972       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6973       {
6974         boolean first_horiz = RND(2);
6975         int new_move_dir = MovDir[x][y];
6976
6977         MovDir[x][y] =
6978           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6979         Moving2Blocked(x, y, &newx, &newy);
6980
6981         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6982           return;
6983
6984         MovDir[x][y] =
6985           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6986         Moving2Blocked(x, y, &newx, &newy);
6987
6988         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6989           return;
6990
6991         MovDir[x][y] = old_move_dir;
6992         return;
6993       }
6994     }
6995     else if (element == EL_SATELLITE)
6996     {
6997       int newx, newy;
6998
6999       MovDelay[x][y] = 1;
7000
7001       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7002       {
7003         boolean first_horiz = RND(2);
7004         int new_move_dir = MovDir[x][y];
7005
7006         MovDir[x][y] =
7007           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7008         Moving2Blocked(x, y, &newx, &newy);
7009
7010         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7011           return;
7012
7013         MovDir[x][y] =
7014           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7015         Moving2Blocked(x, y, &newx, &newy);
7016
7017         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7018           return;
7019
7020         MovDir[x][y] = old_move_dir;
7021         return;
7022       }
7023     }
7024     else if (element == EL_EMC_ANDROID)
7025     {
7026       static int check_pos[16] =
7027       {
7028         -1,             /*  0 => (invalid)          */
7029         7,              /*  1 => MV_LEFT            */
7030         3,              /*  2 => MV_RIGHT           */
7031         -1,             /*  3 => (invalid)          */
7032         1,              /*  4 =>            MV_UP   */
7033         0,              /*  5 => MV_LEFT  | MV_UP   */
7034         2,              /*  6 => MV_RIGHT | MV_UP   */
7035         -1,             /*  7 => (invalid)          */
7036         5,              /*  8 =>            MV_DOWN */
7037         6,              /*  9 => MV_LEFT  | MV_DOWN */
7038         4,              /* 10 => MV_RIGHT | MV_DOWN */
7039         -1,             /* 11 => (invalid)          */
7040         -1,             /* 12 => (invalid)          */
7041         -1,             /* 13 => (invalid)          */
7042         -1,             /* 14 => (invalid)          */
7043         -1,             /* 15 => (invalid)          */
7044       };
7045       static struct
7046       {
7047         int dx, dy;
7048         int dir;
7049       } check_xy[8] =
7050       {
7051         { -1, -1,       MV_LEFT  | MV_UP   },
7052         {  0, -1,                  MV_UP   },
7053         { +1, -1,       MV_RIGHT | MV_UP   },
7054         { +1,  0,       MV_RIGHT           },
7055         { +1, +1,       MV_RIGHT | MV_DOWN },
7056         {  0, +1,                  MV_DOWN },
7057         { -1, +1,       MV_LEFT  | MV_DOWN },
7058         { -1,  0,       MV_LEFT            },
7059       };
7060       int start_pos, check_order;
7061       boolean can_clone = FALSE;
7062       int i;
7063
7064       /* check if there is any free field around current position */
7065       for (i = 0; i < 8; i++)
7066       {
7067         int newx = x + check_xy[i].dx;
7068         int newy = y + check_xy[i].dy;
7069
7070         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7071         {
7072           can_clone = TRUE;
7073
7074           break;
7075         }
7076       }
7077
7078       if (can_clone)            /* randomly find an element to clone */
7079       {
7080         can_clone = FALSE;
7081
7082         start_pos = check_pos[RND(8)];
7083         check_order = (RND(2) ? -1 : +1);
7084
7085         for (i = 0; i < 8; i++)
7086         {
7087           int pos_raw = start_pos + i * check_order;
7088           int pos = (pos_raw + 8) % 8;
7089           int newx = x + check_xy[pos].dx;
7090           int newy = y + check_xy[pos].dy;
7091
7092           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7093           {
7094             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7095             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7096
7097             Store[x][y] = Feld[newx][newy];
7098
7099             can_clone = TRUE;
7100
7101             break;
7102           }
7103         }
7104       }
7105
7106       if (can_clone)            /* randomly find a direction to move */
7107       {
7108         can_clone = FALSE;
7109
7110         start_pos = check_pos[RND(8)];
7111         check_order = (RND(2) ? -1 : +1);
7112
7113         for (i = 0; i < 8; i++)
7114         {
7115           int pos_raw = start_pos + i * check_order;
7116           int pos = (pos_raw + 8) % 8;
7117           int newx = x + check_xy[pos].dx;
7118           int newy = y + check_xy[pos].dy;
7119           int new_move_dir = check_xy[pos].dir;
7120
7121           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7122           {
7123             MovDir[x][y] = new_move_dir;
7124             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7125
7126             can_clone = TRUE;
7127
7128             break;
7129           }
7130         }
7131       }
7132
7133       if (can_clone)            /* cloning and moving successful */
7134         return;
7135
7136       /* cannot clone -- try to move towards player */
7137
7138       start_pos = check_pos[MovDir[x][y] & 0x0f];
7139       check_order = (RND(2) ? -1 : +1);
7140
7141       for (i = 0; i < 3; i++)
7142       {
7143         /* first check start_pos, then previous/next or (next/previous) pos */
7144         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7145         int pos = (pos_raw + 8) % 8;
7146         int newx = x + check_xy[pos].dx;
7147         int newy = y + check_xy[pos].dy;
7148         int new_move_dir = check_xy[pos].dir;
7149
7150         if (IS_PLAYER(newx, newy))
7151           break;
7152
7153         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7154         {
7155           MovDir[x][y] = new_move_dir;
7156           MovDelay[x][y] = level.android_move_time * 8 + 1;
7157
7158           break;
7159         }
7160       }
7161     }
7162   }
7163   else if (move_pattern == MV_TURNING_LEFT ||
7164            move_pattern == MV_TURNING_RIGHT ||
7165            move_pattern == MV_TURNING_LEFT_RIGHT ||
7166            move_pattern == MV_TURNING_RIGHT_LEFT ||
7167            move_pattern == MV_TURNING_RANDOM ||
7168            move_pattern == MV_ALL_DIRECTIONS)
7169   {
7170     boolean can_turn_left =
7171       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7172     boolean can_turn_right =
7173       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7174
7175     if (element_info[element].move_stepsize == 0)       /* "not moving" */
7176       return;
7177
7178     if (move_pattern == MV_TURNING_LEFT)
7179       MovDir[x][y] = left_dir;
7180     else if (move_pattern == MV_TURNING_RIGHT)
7181       MovDir[x][y] = right_dir;
7182     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7183       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7184     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7185       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7186     else if (move_pattern == MV_TURNING_RANDOM)
7187       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7188                       can_turn_right && !can_turn_left ? right_dir :
7189                       RND(2) ? left_dir : right_dir);
7190     else if (can_turn_left && can_turn_right)
7191       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7192     else if (can_turn_left)
7193       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7194     else if (can_turn_right)
7195       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7196     else
7197       MovDir[x][y] = back_dir;
7198
7199     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7200   }
7201   else if (move_pattern == MV_HORIZONTAL ||
7202            move_pattern == MV_VERTICAL)
7203   {
7204     if (move_pattern & old_move_dir)
7205       MovDir[x][y] = back_dir;
7206     else if (move_pattern == MV_HORIZONTAL)
7207       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7208     else if (move_pattern == MV_VERTICAL)
7209       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7210
7211     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7212   }
7213   else if (move_pattern & MV_ANY_DIRECTION)
7214   {
7215     MovDir[x][y] = move_pattern;
7216     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7217   }
7218   else if (move_pattern & MV_WIND_DIRECTION)
7219   {
7220     MovDir[x][y] = game.wind_direction;
7221     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7222   }
7223   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7224   {
7225     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7226       MovDir[x][y] = left_dir;
7227     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7228       MovDir[x][y] = right_dir;
7229
7230     if (MovDir[x][y] != old_move_dir)
7231       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7232   }
7233   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7234   {
7235     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7236       MovDir[x][y] = right_dir;
7237     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7238       MovDir[x][y] = left_dir;
7239
7240     if (MovDir[x][y] != old_move_dir)
7241       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7242   }
7243   else if (move_pattern == MV_TOWARDS_PLAYER ||
7244            move_pattern == MV_AWAY_FROM_PLAYER)
7245   {
7246     int attr_x = -1, attr_y = -1;
7247     int newx, newy;
7248     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7249
7250     if (AllPlayersGone)
7251     {
7252       attr_x = ExitX;
7253       attr_y = ExitY;
7254     }
7255     else
7256     {
7257       int i;
7258
7259       for (i = 0; i < MAX_PLAYERS; i++)
7260       {
7261         struct PlayerInfo *player = &stored_player[i];
7262         int jx = player->jx, jy = player->jy;
7263
7264         if (!player->active)
7265           continue;
7266
7267         if (attr_x == -1 ||
7268             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7269         {
7270           attr_x = jx;
7271           attr_y = jy;
7272         }
7273       }
7274     }
7275
7276     MovDir[x][y] = MV_NONE;
7277     if (attr_x < x)
7278       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7279     else if (attr_x > x)
7280       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7281     if (attr_y < y)
7282       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7283     else if (attr_y > y)
7284       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7285
7286     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7287
7288     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7289     {
7290       boolean first_horiz = RND(2);
7291       int new_move_dir = MovDir[x][y];
7292
7293       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7294       {
7295         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7296         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7297
7298         return;
7299       }
7300
7301       MovDir[x][y] =
7302         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7303       Moving2Blocked(x, y, &newx, &newy);
7304
7305       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7306         return;
7307
7308       MovDir[x][y] =
7309         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7310       Moving2Blocked(x, y, &newx, &newy);
7311
7312       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7313         return;
7314
7315       MovDir[x][y] = old_move_dir;
7316     }
7317   }
7318   else if (move_pattern == MV_WHEN_PUSHED ||
7319            move_pattern == MV_WHEN_DROPPED)
7320   {
7321     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7322       MovDir[x][y] = MV_NONE;
7323
7324     MovDelay[x][y] = 0;
7325   }
7326   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7327   {
7328     static int test_xy[7][2] =
7329     {
7330       { 0, -1 },
7331       { -1, 0 },
7332       { +1, 0 },
7333       { 0, +1 },
7334       { 0, -1 },
7335       { -1, 0 },
7336       { +1, 0 },
7337     };
7338     static int test_dir[7] =
7339     {
7340       MV_UP,
7341       MV_LEFT,
7342       MV_RIGHT,
7343       MV_DOWN,
7344       MV_UP,
7345       MV_LEFT,
7346       MV_RIGHT,
7347     };
7348     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7349     int move_preference = -1000000;     /* start with very low preference */
7350     int new_move_dir = MV_NONE;
7351     int start_test = RND(4);
7352     int i;
7353
7354     for (i = 0; i < NUM_DIRECTIONS; i++)
7355     {
7356       int move_dir = test_dir[start_test + i];
7357       int move_dir_preference;
7358
7359       xx = x + test_xy[start_test + i][0];
7360       yy = y + test_xy[start_test + i][1];
7361
7362       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7363           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7364       {
7365         new_move_dir = move_dir;
7366
7367         break;
7368       }
7369
7370       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7371         continue;
7372
7373       move_dir_preference = -1 * RunnerVisit[xx][yy];
7374       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7375         move_dir_preference = PlayerVisit[xx][yy];
7376
7377       if (move_dir_preference > move_preference)
7378       {
7379         /* prefer field that has not been visited for the longest time */
7380         move_preference = move_dir_preference;
7381         new_move_dir = move_dir;
7382       }
7383       else if (move_dir_preference == move_preference &&
7384                move_dir == old_move_dir)
7385       {
7386         /* prefer last direction when all directions are preferred equally */
7387         move_preference = move_dir_preference;
7388         new_move_dir = move_dir;
7389       }
7390     }
7391
7392     MovDir[x][y] = new_move_dir;
7393     if (old_move_dir != new_move_dir)
7394       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7395   }
7396 }
7397
7398 static void TurnRound(int x, int y)
7399 {
7400   int direction = MovDir[x][y];
7401
7402   TurnRoundExt(x, y);
7403
7404   GfxDir[x][y] = MovDir[x][y];
7405
7406   if (direction != MovDir[x][y])
7407     GfxFrame[x][y] = 0;
7408
7409   if (MovDelay[x][y])
7410     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7411
7412   ResetGfxFrame(x, y, FALSE);
7413 }
7414
7415 static boolean JustBeingPushed(int x, int y)
7416 {
7417   int i;
7418
7419   for (i = 0; i < MAX_PLAYERS; i++)
7420   {
7421     struct PlayerInfo *player = &stored_player[i];
7422
7423     if (player->active && player->is_pushing && player->MovPos)
7424     {
7425       int next_jx = player->jx + (player->jx - player->last_jx);
7426       int next_jy = player->jy + (player->jy - player->last_jy);
7427
7428       if (x == next_jx && y == next_jy)
7429         return TRUE;
7430     }
7431   }
7432
7433   return FALSE;
7434 }
7435
7436 void StartMoving(int x, int y)
7437 {
7438   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7439   int element = Feld[x][y];
7440
7441   if (Stop[x][y])
7442     return;
7443
7444   if (MovDelay[x][y] == 0)
7445     GfxAction[x][y] = ACTION_DEFAULT;
7446
7447   if (CAN_FALL(element) && y < lev_fieldy - 1)
7448   {
7449     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7450         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7451       if (JustBeingPushed(x, y))
7452         return;
7453
7454     if (element == EL_QUICKSAND_FULL)
7455     {
7456       if (IS_FREE(x, y + 1))
7457       {
7458         InitMovingField(x, y, MV_DOWN);
7459         started_moving = TRUE;
7460
7461         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7462 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7463         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7464           Store[x][y] = EL_ROCK;
7465 #else
7466         Store[x][y] = EL_ROCK;
7467 #endif
7468
7469         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7470       }
7471       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7472       {
7473         if (!MovDelay[x][y])
7474           MovDelay[x][y] = TILEY + 1;
7475
7476         if (MovDelay[x][y])
7477         {
7478           MovDelay[x][y]--;
7479           if (MovDelay[x][y])
7480             return;
7481         }
7482
7483         Feld[x][y] = EL_QUICKSAND_EMPTY;
7484         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7485         Store[x][y + 1] = Store[x][y];
7486         Store[x][y] = 0;
7487
7488         PlayLevelSoundAction(x, y, ACTION_FILLING);
7489       }
7490     }
7491     else if (element == EL_QUICKSAND_FAST_FULL)
7492     {
7493       if (IS_FREE(x, y + 1))
7494       {
7495         InitMovingField(x, y, MV_DOWN);
7496         started_moving = TRUE;
7497
7498         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7499 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7500         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7501           Store[x][y] = EL_ROCK;
7502 #else
7503         Store[x][y] = EL_ROCK;
7504 #endif
7505
7506         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7507       }
7508       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7509       {
7510         if (!MovDelay[x][y])
7511           MovDelay[x][y] = TILEY + 1;
7512
7513         if (MovDelay[x][y])
7514         {
7515           MovDelay[x][y]--;
7516           if (MovDelay[x][y])
7517             return;
7518         }
7519
7520         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7521         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7522         Store[x][y + 1] = Store[x][y];
7523         Store[x][y] = 0;
7524
7525         PlayLevelSoundAction(x, y, ACTION_FILLING);
7526       }
7527     }
7528     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7529              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7530     {
7531       InitMovingField(x, y, MV_DOWN);
7532       started_moving = TRUE;
7533
7534       Feld[x][y] = EL_QUICKSAND_FILLING;
7535       Store[x][y] = element;
7536
7537       PlayLevelSoundAction(x, y, ACTION_FILLING);
7538     }
7539     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7540              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7541     {
7542       InitMovingField(x, y, MV_DOWN);
7543       started_moving = TRUE;
7544
7545       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7546       Store[x][y] = element;
7547
7548       PlayLevelSoundAction(x, y, ACTION_FILLING);
7549     }
7550     else if (element == EL_MAGIC_WALL_FULL)
7551     {
7552       if (IS_FREE(x, y + 1))
7553       {
7554         InitMovingField(x, y, MV_DOWN);
7555         started_moving = TRUE;
7556
7557         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7558         Store[x][y] = EL_CHANGED(Store[x][y]);
7559       }
7560       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7561       {
7562         if (!MovDelay[x][y])
7563           MovDelay[x][y] = TILEY/4 + 1;
7564
7565         if (MovDelay[x][y])
7566         {
7567           MovDelay[x][y]--;
7568           if (MovDelay[x][y])
7569             return;
7570         }
7571
7572         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7573         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7574         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7575         Store[x][y] = 0;
7576       }
7577     }
7578     else if (element == EL_BD_MAGIC_WALL_FULL)
7579     {
7580       if (IS_FREE(x, y + 1))
7581       {
7582         InitMovingField(x, y, MV_DOWN);
7583         started_moving = TRUE;
7584
7585         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7586         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7587       }
7588       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7589       {
7590         if (!MovDelay[x][y])
7591           MovDelay[x][y] = TILEY/4 + 1;
7592
7593         if (MovDelay[x][y])
7594         {
7595           MovDelay[x][y]--;
7596           if (MovDelay[x][y])
7597             return;
7598         }
7599
7600         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7601         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7602         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7603         Store[x][y] = 0;
7604       }
7605     }
7606     else if (element == EL_DC_MAGIC_WALL_FULL)
7607     {
7608       if (IS_FREE(x, y + 1))
7609       {
7610         InitMovingField(x, y, MV_DOWN);
7611         started_moving = TRUE;
7612
7613         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7614         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7615       }
7616       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7617       {
7618         if (!MovDelay[x][y])
7619           MovDelay[x][y] = TILEY/4 + 1;
7620
7621         if (MovDelay[x][y])
7622         {
7623           MovDelay[x][y]--;
7624           if (MovDelay[x][y])
7625             return;
7626         }
7627
7628         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7629         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7630         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7631         Store[x][y] = 0;
7632       }
7633     }
7634     else if ((CAN_PASS_MAGIC_WALL(element) &&
7635               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7636                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7637              (CAN_PASS_DC_MAGIC_WALL(element) &&
7638               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7639
7640     {
7641       InitMovingField(x, y, MV_DOWN);
7642       started_moving = TRUE;
7643
7644       Feld[x][y] =
7645         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7646          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7647          EL_DC_MAGIC_WALL_FILLING);
7648       Store[x][y] = element;
7649     }
7650     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7651     {
7652       SplashAcid(x, y + 1);
7653
7654       InitMovingField(x, y, MV_DOWN);
7655       started_moving = TRUE;
7656
7657       Store[x][y] = EL_ACID;
7658     }
7659     else if (
7660 #if USE_FIX_IMPACT_COLLISION
7661              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7662               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7663 #else
7664              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7665               CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
7666 #endif
7667              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7668               CAN_FALL(element) && WasJustFalling[x][y] &&
7669               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7670
7671              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7672               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7673               (Feld[x][y + 1] == EL_BLOCKED)))
7674     {
7675       /* this is needed for a special case not covered by calling "Impact()"
7676          from "ContinueMoving()": if an element moves to a tile directly below
7677          another element which was just falling on that tile (which was empty
7678          in the previous frame), the falling element above would just stop
7679          instead of smashing the element below (in previous version, the above
7680          element was just checked for "moving" instead of "falling", resulting
7681          in incorrect smashes caused by horizontal movement of the above
7682          element; also, the case of the player being the element to smash was
7683          simply not covered here... :-/ ) */
7684
7685       CheckCollision[x][y] = 0;
7686       CheckImpact[x][y] = 0;
7687
7688       Impact(x, y);
7689     }
7690     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7691     {
7692       if (MovDir[x][y] == MV_NONE)
7693       {
7694         InitMovingField(x, y, MV_DOWN);
7695         started_moving = TRUE;
7696       }
7697     }
7698     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7699     {
7700       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7701         MovDir[x][y] = MV_DOWN;
7702
7703       InitMovingField(x, y, MV_DOWN);
7704       started_moving = TRUE;
7705     }
7706     else if (element == EL_AMOEBA_DROP)
7707     {
7708       Feld[x][y] = EL_AMOEBA_GROWING;
7709       Store[x][y] = EL_AMOEBA_WET;
7710     }
7711     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7712               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7713              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7714              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7715     {
7716       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7717                                 (IS_FREE(x - 1, y + 1) ||
7718                                  Feld[x - 1][y + 1] == EL_ACID));
7719       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7720                                 (IS_FREE(x + 1, y + 1) ||
7721                                  Feld[x + 1][y + 1] == EL_ACID));
7722       boolean can_fall_any  = (can_fall_left || can_fall_right);
7723       boolean can_fall_both = (can_fall_left && can_fall_right);
7724       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7725
7726 #if USE_NEW_ALL_SLIPPERY
7727       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7728       {
7729         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7730           can_fall_right = FALSE;
7731         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7732           can_fall_left = FALSE;
7733         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7734           can_fall_right = FALSE;
7735         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7736           can_fall_left = FALSE;
7737
7738         can_fall_any  = (can_fall_left || can_fall_right);
7739         can_fall_both = FALSE;
7740       }
7741 #else
7742       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
7743       {
7744         if (slippery_type == SLIPPERY_ONLY_LEFT)
7745           can_fall_right = FALSE;
7746         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7747           can_fall_left = FALSE;
7748         else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7749           can_fall_right = FALSE;
7750         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7751           can_fall_left = FALSE;
7752
7753         can_fall_any  = (can_fall_left || can_fall_right);
7754         can_fall_both = (can_fall_left && can_fall_right);
7755       }
7756 #endif
7757
7758 #if USE_NEW_ALL_SLIPPERY
7759 #else
7760 #if USE_NEW_SP_SLIPPERY
7761       /* !!! better use the same properties as for custom elements here !!! */
7762       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
7763                can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
7764       {
7765         can_fall_right = FALSE;         /* slip down on left side */
7766         can_fall_both = FALSE;
7767       }
7768 #endif
7769 #endif
7770
7771 #if USE_NEW_ALL_SLIPPERY
7772       if (can_fall_both)
7773       {
7774         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7775           can_fall_right = FALSE;       /* slip down on left side */
7776         else
7777           can_fall_left = !(can_fall_right = RND(2));
7778
7779         can_fall_both = FALSE;
7780       }
7781 #else
7782       if (can_fall_both)
7783       {
7784         if (game.emulation == EMU_BOULDERDASH ||
7785             element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7786           can_fall_right = FALSE;       /* slip down on left side */
7787         else
7788           can_fall_left = !(can_fall_right = RND(2));
7789
7790         can_fall_both = FALSE;
7791       }
7792 #endif
7793
7794       if (can_fall_any)
7795       {
7796         /* if not determined otherwise, prefer left side for slipping down */
7797         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7798         started_moving = TRUE;
7799       }
7800     }
7801 #if 0
7802     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
7803 #else
7804     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7805 #endif
7806     {
7807       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7808       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7809       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7810       int belt_dir = game.belt_dir[belt_nr];
7811
7812       if ((belt_dir == MV_LEFT  && left_is_free) ||
7813           (belt_dir == MV_RIGHT && right_is_free))
7814       {
7815         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7816
7817         InitMovingField(x, y, belt_dir);
7818         started_moving = TRUE;
7819
7820         Pushed[x][y] = TRUE;
7821         Pushed[nextx][y] = TRUE;
7822
7823         GfxAction[x][y] = ACTION_DEFAULT;
7824       }
7825       else
7826       {
7827         MovDir[x][y] = 0;       /* if element was moving, stop it */
7828       }
7829     }
7830   }
7831
7832   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7833 #if 0
7834   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
7835 #else
7836   if (CAN_MOVE(element) && !started_moving)
7837 #endif
7838   {
7839     int move_pattern = element_info[element].move_pattern;
7840     int newx, newy;
7841
7842 #if 0
7843 #if DEBUG
7844     if (MovDir[x][y] == MV_NONE)
7845     {
7846       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
7847              x, y, element, element_info[element].token_name);
7848       printf("StartMoving(): This should never happen!\n");
7849     }
7850 #endif
7851 #endif
7852
7853     Moving2Blocked(x, y, &newx, &newy);
7854
7855     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7856       return;
7857
7858     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7859         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7860     {
7861       WasJustMoving[x][y] = 0;
7862       CheckCollision[x][y] = 0;
7863
7864       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7865
7866       if (Feld[x][y] != element)        /* element has changed */
7867         return;
7868     }
7869
7870     if (!MovDelay[x][y])        /* start new movement phase */
7871     {
7872       /* all objects that can change their move direction after each step
7873          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7874
7875       if (element != EL_YAMYAM &&
7876           element != EL_DARK_YAMYAM &&
7877           element != EL_PACMAN &&
7878           !(move_pattern & MV_ANY_DIRECTION) &&
7879           move_pattern != MV_TURNING_LEFT &&
7880           move_pattern != MV_TURNING_RIGHT &&
7881           move_pattern != MV_TURNING_LEFT_RIGHT &&
7882           move_pattern != MV_TURNING_RIGHT_LEFT &&
7883           move_pattern != MV_TURNING_RANDOM)
7884       {
7885         TurnRound(x, y);
7886
7887         if (MovDelay[x][y] && (element == EL_BUG ||
7888                                element == EL_SPACESHIP ||
7889                                element == EL_SP_SNIKSNAK ||
7890                                element == EL_SP_ELECTRON ||
7891                                element == EL_MOLE))
7892           DrawLevelField(x, y);
7893       }
7894     }
7895
7896     if (MovDelay[x][y])         /* wait some time before next movement */
7897     {
7898       MovDelay[x][y]--;
7899
7900       if (element == EL_ROBOT ||
7901           element == EL_YAMYAM ||
7902           element == EL_DARK_YAMYAM)
7903       {
7904         DrawLevelElementAnimationIfNeeded(x, y, element);
7905         PlayLevelSoundAction(x, y, ACTION_WAITING);
7906       }
7907       else if (element == EL_SP_ELECTRON)
7908         DrawLevelElementAnimationIfNeeded(x, y, element);
7909       else if (element == EL_DRAGON)
7910       {
7911         int i;
7912         int dir = MovDir[x][y];
7913         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7914         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7915         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7916                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7917                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7918                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7919         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7920
7921         GfxAction[x][y] = ACTION_ATTACKING;
7922
7923         if (IS_PLAYER(x, y))
7924           DrawPlayerField(x, y);
7925         else
7926           DrawLevelField(x, y);
7927
7928         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7929
7930         for (i = 1; i <= 3; i++)
7931         {
7932           int xx = x + i * dx;
7933           int yy = y + i * dy;
7934           int sx = SCREENX(xx);
7935           int sy = SCREENY(yy);
7936           int flame_graphic = graphic + (i - 1);
7937
7938           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7939             break;
7940
7941           if (MovDelay[x][y])
7942           {
7943             int flamed = MovingOrBlocked2Element(xx, yy);
7944
7945             /* !!! */
7946 #if 0
7947             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7948               Bang(xx, yy);
7949             else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
7950               RemoveMovingField(xx, yy);
7951             else
7952               RemoveField(xx, yy);
7953 #else
7954             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7955               Bang(xx, yy);
7956             else
7957               RemoveMovingField(xx, yy);
7958 #endif
7959
7960             ChangeDelay[xx][yy] = 0;
7961
7962             Feld[xx][yy] = EL_FLAMES;
7963
7964             if (IN_SCR_FIELD(sx, sy))
7965             {
7966               DrawLevelFieldCrumbledSand(xx, yy);
7967               DrawGraphic(sx, sy, flame_graphic, frame);
7968             }
7969           }
7970           else
7971           {
7972             if (Feld[xx][yy] == EL_FLAMES)
7973               Feld[xx][yy] = EL_EMPTY;
7974             DrawLevelField(xx, yy);
7975           }
7976         }
7977       }
7978
7979       if (MovDelay[x][y])       /* element still has to wait some time */
7980       {
7981         PlayLevelSoundAction(x, y, ACTION_WAITING);
7982
7983         return;
7984       }
7985     }
7986
7987     /* now make next step */
7988
7989     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7990
7991     if (DONT_COLLIDE_WITH(element) &&
7992         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7993         !PLAYER_ENEMY_PROTECTED(newx, newy))
7994     {
7995       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7996
7997       return;
7998     }
7999
8000     else if (CAN_MOVE_INTO_ACID(element) &&
8001              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8002              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8003              (MovDir[x][y] == MV_DOWN ||
8004               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8005     {
8006       SplashAcid(newx, newy);
8007       Store[x][y] = EL_ACID;
8008     }
8009     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8010     {
8011       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8012           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8013           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8014           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8015       {
8016         RemoveField(x, y);
8017         DrawLevelField(x, y);
8018
8019         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8020         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8021           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8022
8023         local_player->friends_still_needed--;
8024         if (!local_player->friends_still_needed &&
8025             !local_player->GameOver && AllPlayersGone)
8026           PlayerWins(local_player);
8027
8028         return;
8029       }
8030       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8031       {
8032         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8033           DrawLevelField(newx, newy);
8034         else
8035           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8036       }
8037       else if (!IS_FREE(newx, newy))
8038       {
8039         GfxAction[x][y] = ACTION_WAITING;
8040
8041         if (IS_PLAYER(x, y))
8042           DrawPlayerField(x, y);
8043         else
8044           DrawLevelField(x, y);
8045
8046         return;
8047       }
8048     }
8049     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8050     {
8051       if (IS_FOOD_PIG(Feld[newx][newy]))
8052       {
8053         if (IS_MOVING(newx, newy))
8054           RemoveMovingField(newx, newy);
8055         else
8056         {
8057           Feld[newx][newy] = EL_EMPTY;
8058           DrawLevelField(newx, newy);
8059         }
8060
8061         PlayLevelSound(x, y, SND_PIG_DIGGING);
8062       }
8063       else if (!IS_FREE(newx, newy))
8064       {
8065         if (IS_PLAYER(x, y))
8066           DrawPlayerField(x, y);
8067         else
8068           DrawLevelField(x, y);
8069
8070         return;
8071       }
8072     }
8073     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8074     {
8075       if (Store[x][y] != EL_EMPTY)
8076       {
8077         boolean can_clone = FALSE;
8078         int xx, yy;
8079
8080         /* check if element to clone is still there */
8081         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8082         {
8083           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8084           {
8085             can_clone = TRUE;
8086
8087             break;
8088           }
8089         }
8090
8091         /* cannot clone or target field not free anymore -- do not clone */
8092         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8093           Store[x][y] = EL_EMPTY;
8094       }
8095
8096       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8097       {
8098         if (IS_MV_DIAGONAL(MovDir[x][y]))
8099         {
8100           int diagonal_move_dir = MovDir[x][y];
8101           int stored = Store[x][y];
8102           int change_delay = 8;
8103           int graphic;
8104
8105           /* android is moving diagonally */
8106
8107           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8108
8109           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8110           GfxElement[x][y] = EL_EMC_ANDROID;
8111           GfxAction[x][y] = ACTION_SHRINKING;
8112           GfxDir[x][y] = diagonal_move_dir;
8113           ChangeDelay[x][y] = change_delay;
8114
8115           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8116                                    GfxDir[x][y]);
8117
8118           DrawLevelGraphicAnimation(x, y, graphic);
8119           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8120
8121           if (Feld[newx][newy] == EL_ACID)
8122           {
8123             SplashAcid(newx, newy);
8124
8125             return;
8126           }
8127
8128           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8129
8130           Store[newx][newy] = EL_EMC_ANDROID;
8131           GfxElement[newx][newy] = EL_EMC_ANDROID;
8132           GfxAction[newx][newy] = ACTION_GROWING;
8133           GfxDir[newx][newy] = diagonal_move_dir;
8134           ChangeDelay[newx][newy] = change_delay;
8135
8136           graphic = el_act_dir2img(GfxElement[newx][newy],
8137                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8138
8139           DrawLevelGraphicAnimation(newx, newy, graphic);
8140           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8141
8142           return;
8143         }
8144         else
8145         {
8146           Feld[newx][newy] = EL_EMPTY;
8147           DrawLevelField(newx, newy);
8148
8149           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8150         }
8151       }
8152       else if (!IS_FREE(newx, newy))
8153       {
8154 #if 0
8155         if (IS_PLAYER(x, y))
8156           DrawPlayerField(x, y);
8157         else
8158           DrawLevelField(x, y);
8159 #endif
8160
8161         return;
8162       }
8163     }
8164     else if (IS_CUSTOM_ELEMENT(element) &&
8165              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8166     {
8167       int new_element = Feld[newx][newy];
8168
8169       if (!IS_FREE(newx, newy))
8170       {
8171         int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8172                       IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8173                       ACTION_BREAKING);
8174
8175         /* no element can dig solid indestructible elements */
8176         if (IS_INDESTRUCTIBLE(new_element) &&
8177             !IS_DIGGABLE(new_element) &&
8178             !IS_COLLECTIBLE(new_element))
8179           return;
8180
8181         if (AmoebaNr[newx][newy] &&
8182             (new_element == EL_AMOEBA_FULL ||
8183              new_element == EL_BD_AMOEBA ||
8184              new_element == EL_AMOEBA_GROWING))
8185         {
8186           AmoebaCnt[AmoebaNr[newx][newy]]--;
8187           AmoebaCnt2[AmoebaNr[newx][newy]]--;
8188         }
8189
8190         if (IS_MOVING(newx, newy))
8191           RemoveMovingField(newx, newy);
8192         else
8193         {
8194           RemoveField(newx, newy);
8195           DrawLevelField(newx, newy);
8196         }
8197
8198         /* if digged element was about to explode, prevent the explosion */
8199         ExplodeField[newx][newy] = EX_TYPE_NONE;
8200
8201         PlayLevelSoundAction(x, y, action);
8202       }
8203
8204       Store[newx][newy] = EL_EMPTY;
8205 #if 1
8206       /* this makes it possible to leave the removed element again */
8207       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8208         Store[newx][newy] = new_element;
8209 #else
8210       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8211       {
8212         int move_leave_element = element_info[element].move_leave_element;
8213
8214         /* this makes it possible to leave the removed element again */
8215         Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8216                              new_element : move_leave_element);
8217       }
8218 #endif
8219
8220       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8221       {
8222         RunnerVisit[x][y] = FrameCounter;
8223         PlayerVisit[x][y] /= 8;         /* expire player visit path */
8224       }
8225     }
8226     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8227     {
8228       if (!IS_FREE(newx, newy))
8229       {
8230         if (IS_PLAYER(x, y))
8231           DrawPlayerField(x, y);
8232         else
8233           DrawLevelField(x, y);
8234
8235         return;
8236       }
8237       else
8238       {
8239         boolean wanna_flame = !RND(10);
8240         int dx = newx - x, dy = newy - y;
8241         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8242         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8243         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8244                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8245         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8246                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8247
8248         if ((wanna_flame ||
8249              IS_CLASSIC_ENEMY(element1) ||
8250              IS_CLASSIC_ENEMY(element2)) &&
8251             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8252             element1 != EL_FLAMES && element2 != EL_FLAMES)
8253         {
8254           ResetGfxAnimation(x, y);
8255           GfxAction[x][y] = ACTION_ATTACKING;
8256
8257           if (IS_PLAYER(x, y))
8258             DrawPlayerField(x, y);
8259           else
8260             DrawLevelField(x, y);
8261
8262           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8263
8264           MovDelay[x][y] = 50;
8265
8266           /* !!! */
8267 #if 0
8268           RemoveField(newx, newy);
8269 #endif
8270           Feld[newx][newy] = EL_FLAMES;
8271           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8272           {
8273 #if 0
8274             RemoveField(newx1, newy1);
8275 #endif
8276             Feld[newx1][newy1] = EL_FLAMES;
8277           }
8278           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8279           {
8280 #if 0
8281             RemoveField(newx2, newy2);
8282 #endif
8283             Feld[newx2][newy2] = EL_FLAMES;
8284           }
8285
8286           return;
8287         }
8288       }
8289     }
8290     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8291              Feld[newx][newy] == EL_DIAMOND)
8292     {
8293       if (IS_MOVING(newx, newy))
8294         RemoveMovingField(newx, newy);
8295       else
8296       {
8297         Feld[newx][newy] = EL_EMPTY;
8298         DrawLevelField(newx, newy);
8299       }
8300
8301       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8302     }
8303     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8304              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8305     {
8306       if (AmoebaNr[newx][newy])
8307       {
8308         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8309         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8310             Feld[newx][newy] == EL_BD_AMOEBA)
8311           AmoebaCnt[AmoebaNr[newx][newy]]--;
8312       }
8313
8314 #if 0
8315       /* !!! test !!! */
8316       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8317       {
8318         RemoveMovingField(newx, newy);
8319       }
8320 #else
8321       if (IS_MOVING(newx, newy))
8322       {
8323         RemoveMovingField(newx, newy);
8324       }
8325 #endif
8326       else
8327       {
8328         Feld[newx][newy] = EL_EMPTY;
8329         DrawLevelField(newx, newy);
8330       }
8331
8332       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8333     }
8334     else if ((element == EL_PACMAN || element == EL_MOLE)
8335              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8336     {
8337       if (AmoebaNr[newx][newy])
8338       {
8339         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8340         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8341             Feld[newx][newy] == EL_BD_AMOEBA)
8342           AmoebaCnt[AmoebaNr[newx][newy]]--;
8343       }
8344
8345       if (element == EL_MOLE)
8346       {
8347         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8348         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8349
8350         ResetGfxAnimation(x, y);
8351         GfxAction[x][y] = ACTION_DIGGING;
8352         DrawLevelField(x, y);
8353
8354         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
8355
8356         return;                         /* wait for shrinking amoeba */
8357       }
8358       else      /* element == EL_PACMAN */
8359       {
8360         Feld[newx][newy] = EL_EMPTY;
8361         DrawLevelField(newx, newy);
8362         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8363       }
8364     }
8365     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8366              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8367               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8368     {
8369       /* wait for shrinking amoeba to completely disappear */
8370       return;
8371     }
8372     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8373     {
8374       /* object was running against a wall */
8375
8376       TurnRound(x, y);
8377
8378 #if 0
8379       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8380       if (move_pattern & MV_ANY_DIRECTION &&
8381           move_pattern == MovDir[x][y])
8382       {
8383         int blocking_element =
8384           (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8385
8386         CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8387                                  MovDir[x][y]);
8388
8389         element = Feld[x][y];   /* element might have changed */
8390       }
8391 #endif
8392
8393       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8394         DrawLevelElementAnimation(x, y, element);
8395
8396       if (DONT_TOUCH(element))
8397         TestIfBadThingTouchesPlayer(x, y);
8398
8399       return;
8400     }
8401
8402     InitMovingField(x, y, MovDir[x][y]);
8403
8404     PlayLevelSoundAction(x, y, ACTION_MOVING);
8405   }
8406
8407   if (MovDir[x][y])
8408     ContinueMoving(x, y);
8409 }
8410
8411 void ContinueMoving(int x, int y)
8412 {
8413   int element = Feld[x][y];
8414   struct ElementInfo *ei = &element_info[element];
8415   int direction = MovDir[x][y];
8416   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8417   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8418   int newx = x + dx, newy = y + dy;
8419   int stored = Store[x][y];
8420   int stored_new = Store[newx][newy];
8421   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8422   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8423   boolean last_line = (newy == lev_fieldy - 1);
8424
8425   MovPos[x][y] += getElementMoveStepsize(x, y);
8426
8427   if (pushed_by_player) /* special case: moving object pushed by player */
8428     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8429
8430   if (ABS(MovPos[x][y]) < TILEX)
8431   {
8432 #if 0
8433     int ee = Feld[x][y];
8434     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8435     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8436
8437     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8438            x, y, ABS(MovPos[x][y]),
8439            ee, gg, ff,
8440            GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8441 #endif
8442
8443     DrawLevelField(x, y);
8444
8445     return;     /* element is still moving */
8446   }
8447
8448   /* element reached destination field */
8449
8450   Feld[x][y] = EL_EMPTY;
8451   Feld[newx][newy] = element;
8452   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8453
8454   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8455   {
8456     element = Feld[newx][newy] = EL_ACID;
8457   }
8458   else if (element == EL_MOLE)
8459   {
8460     Feld[x][y] = EL_SAND;
8461
8462     DrawLevelFieldCrumbledSandNeighbours(x, y);
8463   }
8464   else if (element == EL_QUICKSAND_FILLING)
8465   {
8466     element = Feld[newx][newy] = get_next_element(element);
8467     Store[newx][newy] = Store[x][y];
8468   }
8469   else if (element == EL_QUICKSAND_EMPTYING)
8470   {
8471     Feld[x][y] = get_next_element(element);
8472     element = Feld[newx][newy] = Store[x][y];
8473   }
8474   else if (element == EL_QUICKSAND_FAST_FILLING)
8475   {
8476     element = Feld[newx][newy] = get_next_element(element);
8477     Store[newx][newy] = Store[x][y];
8478   }
8479   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8480   {
8481     Feld[x][y] = get_next_element(element);
8482     element = Feld[newx][newy] = Store[x][y];
8483   }
8484   else if (element == EL_MAGIC_WALL_FILLING)
8485   {
8486     element = Feld[newx][newy] = get_next_element(element);
8487     if (!game.magic_wall_active)
8488       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8489     Store[newx][newy] = Store[x][y];
8490   }
8491   else if (element == EL_MAGIC_WALL_EMPTYING)
8492   {
8493     Feld[x][y] = get_next_element(element);
8494     if (!game.magic_wall_active)
8495       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8496     element = Feld[newx][newy] = Store[x][y];
8497
8498 #if USE_NEW_CUSTOM_VALUE
8499     InitField(newx, newy, FALSE);
8500 #endif
8501   }
8502   else if (element == EL_BD_MAGIC_WALL_FILLING)
8503   {
8504     element = Feld[newx][newy] = get_next_element(element);
8505     if (!game.magic_wall_active)
8506       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8507     Store[newx][newy] = Store[x][y];
8508   }
8509   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8510   {
8511     Feld[x][y] = get_next_element(element);
8512     if (!game.magic_wall_active)
8513       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8514     element = Feld[newx][newy] = Store[x][y];
8515
8516 #if USE_NEW_CUSTOM_VALUE
8517     InitField(newx, newy, FALSE);
8518 #endif
8519   }
8520   else if (element == EL_DC_MAGIC_WALL_FILLING)
8521   {
8522     element = Feld[newx][newy] = get_next_element(element);
8523     if (!game.magic_wall_active)
8524       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8525     Store[newx][newy] = Store[x][y];
8526   }
8527   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8528   {
8529     Feld[x][y] = get_next_element(element);
8530     if (!game.magic_wall_active)
8531       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8532     element = Feld[newx][newy] = Store[x][y];
8533
8534 #if USE_NEW_CUSTOM_VALUE
8535     InitField(newx, newy, FALSE);
8536 #endif
8537   }
8538   else if (element == EL_AMOEBA_DROPPING)
8539   {
8540     Feld[x][y] = get_next_element(element);
8541     element = Feld[newx][newy] = Store[x][y];
8542   }
8543   else if (element == EL_SOKOBAN_OBJECT)
8544   {
8545     if (Back[x][y])
8546       Feld[x][y] = Back[x][y];
8547
8548     if (Back[newx][newy])
8549       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8550
8551     Back[x][y] = Back[newx][newy] = 0;
8552   }
8553
8554   Store[x][y] = EL_EMPTY;
8555   MovPos[x][y] = 0;
8556   MovDir[x][y] = 0;
8557   MovDelay[x][y] = 0;
8558
8559   MovDelay[newx][newy] = 0;
8560
8561   if (CAN_CHANGE_OR_HAS_ACTION(element))
8562   {
8563     /* copy element change control values to new field */
8564     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8565     ChangePage[newx][newy]  = ChangePage[x][y];
8566     ChangeCount[newx][newy] = ChangeCount[x][y];
8567     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8568   }
8569
8570 #if USE_NEW_CUSTOM_VALUE
8571     CustomValue[newx][newy] = CustomValue[x][y];
8572 #endif
8573
8574   ChangeDelay[x][y] = 0;
8575   ChangePage[x][y] = -1;
8576   ChangeCount[x][y] = 0;
8577   ChangeEvent[x][y] = -1;
8578
8579 #if USE_NEW_CUSTOM_VALUE
8580   CustomValue[x][y] = 0;
8581 #endif
8582
8583   /* copy animation control values to new field */
8584   GfxFrame[newx][newy]  = GfxFrame[x][y];
8585   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8586   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8587   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8588
8589   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8590
8591   /* some elements can leave other elements behind after moving */
8592 #if 1
8593   if (ei->move_leave_element != EL_EMPTY &&
8594       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8595       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8596 #else
8597   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
8598       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8599       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8600 #endif
8601   {
8602     int move_leave_element = ei->move_leave_element;
8603
8604 #if 1
8605 #if 1
8606     /* this makes it possible to leave the removed element again */
8607     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8608       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8609 #else
8610     /* this makes it possible to leave the removed element again */
8611     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8612       move_leave_element = stored;
8613 #endif
8614 #else
8615     /* this makes it possible to leave the removed element again */
8616     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
8617         ei->move_leave_element == EL_TRIGGER_ELEMENT)
8618       move_leave_element = stored;
8619 #endif
8620
8621     Feld[x][y] = move_leave_element;
8622
8623     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8624       MovDir[x][y] = direction;
8625
8626     InitField(x, y, FALSE);
8627
8628     if (GFX_CRUMBLED(Feld[x][y]))
8629       DrawLevelFieldCrumbledSandNeighbours(x, y);
8630
8631     if (ELEM_IS_PLAYER(move_leave_element))
8632       RelocatePlayer(x, y, move_leave_element);
8633   }
8634
8635   /* do this after checking for left-behind element */
8636   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8637
8638   if (!CAN_MOVE(element) ||
8639       (CAN_FALL(element) && direction == MV_DOWN &&
8640        (element == EL_SPRING ||
8641         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8642         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8643     GfxDir[x][y] = MovDir[newx][newy] = 0;
8644
8645   DrawLevelField(x, y);
8646   DrawLevelField(newx, newy);
8647
8648   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8649
8650   /* prevent pushed element from moving on in pushed direction */
8651   if (pushed_by_player && CAN_MOVE(element) &&
8652       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8653       !(element_info[element].move_pattern & direction))
8654     TurnRound(newx, newy);
8655
8656   /* prevent elements on conveyor belt from moving on in last direction */
8657   if (pushed_by_conveyor && CAN_FALL(element) &&
8658       direction & MV_HORIZONTAL)
8659     MovDir[newx][newy] = 0;
8660
8661   if (!pushed_by_player)
8662   {
8663     int nextx = newx + dx, nexty = newy + dy;
8664     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8665
8666     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8667
8668     if (CAN_FALL(element) && direction == MV_DOWN)
8669       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8670
8671     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8672       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8673
8674 #if USE_FIX_IMPACT_COLLISION
8675     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8676       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8677 #endif
8678   }
8679
8680   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8681   {
8682     TestIfBadThingTouchesPlayer(newx, newy);
8683     TestIfBadThingTouchesFriend(newx, newy);
8684
8685     if (!IS_CUSTOM_ELEMENT(element))
8686       TestIfBadThingTouchesOtherBadThing(newx, newy);
8687   }
8688   else if (element == EL_PENGUIN)
8689     TestIfFriendTouchesBadThing(newx, newy);
8690
8691   /* give the player one last chance (one more frame) to move away */
8692   if (CAN_FALL(element) && direction == MV_DOWN &&
8693       (last_line || (!IS_FREE(x, newy + 1) &&
8694                      (!IS_PLAYER(x, newy + 1) ||
8695                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8696     Impact(x, newy);
8697
8698   if (pushed_by_player && !game.use_change_when_pushing_bug)
8699   {
8700     int push_side = MV_DIR_OPPOSITE(direction);
8701     struct PlayerInfo *player = PLAYERINFO(x, y);
8702
8703     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8704                                player->index_bit, push_side);
8705     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8706                                         player->index_bit, push_side);
8707   }
8708
8709   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8710     MovDelay[newx][newy] = 1;
8711
8712   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8713
8714   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8715
8716 #if 0
8717   if (ChangePage[newx][newy] != -1)             /* delayed change */
8718   {
8719     int page = ChangePage[newx][newy];
8720     struct ElementChangeInfo *change = &ei->change_page[page];
8721
8722     ChangePage[newx][newy] = -1;
8723
8724     if (change->can_change)
8725     {
8726       if (ChangeElement(newx, newy, element, page))
8727       {
8728         if (change->post_change_function)
8729           change->post_change_function(newx, newy);
8730       }
8731     }
8732
8733     if (change->has_action)
8734       ExecuteCustomElementAction(newx, newy, element, page);
8735   }
8736 #endif
8737
8738   TestIfElementHitsCustomElement(newx, newy, direction);
8739   TestIfPlayerTouchesCustomElement(newx, newy);
8740   TestIfElementTouchesCustomElement(newx, newy);
8741
8742   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8743       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8744     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8745                              MV_DIR_OPPOSITE(direction));
8746 }
8747
8748 int AmoebeNachbarNr(int ax, int ay)
8749 {
8750   int i;
8751   int element = Feld[ax][ay];
8752   int group_nr = 0;
8753   static int xy[4][2] =
8754   {
8755     { 0, -1 },
8756     { -1, 0 },
8757     { +1, 0 },
8758     { 0, +1 }
8759   };
8760
8761   for (i = 0; i < NUM_DIRECTIONS; i++)
8762   {
8763     int x = ax + xy[i][0];
8764     int y = ay + xy[i][1];
8765
8766     if (!IN_LEV_FIELD(x, y))
8767       continue;
8768
8769     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8770       group_nr = AmoebaNr[x][y];
8771   }
8772
8773   return group_nr;
8774 }
8775
8776 void AmoebenVereinigen(int ax, int ay)
8777 {
8778   int i, x, y, xx, yy;
8779   int new_group_nr = AmoebaNr[ax][ay];
8780   static int xy[4][2] =
8781   {
8782     { 0, -1 },
8783     { -1, 0 },
8784     { +1, 0 },
8785     { 0, +1 }
8786   };
8787
8788   if (new_group_nr == 0)
8789     return;
8790
8791   for (i = 0; i < NUM_DIRECTIONS; i++)
8792   {
8793     x = ax + xy[i][0];
8794     y = ay + xy[i][1];
8795
8796     if (!IN_LEV_FIELD(x, y))
8797       continue;
8798
8799     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8800          Feld[x][y] == EL_BD_AMOEBA ||
8801          Feld[x][y] == EL_AMOEBA_DEAD) &&
8802         AmoebaNr[x][y] != new_group_nr)
8803     {
8804       int old_group_nr = AmoebaNr[x][y];
8805
8806       if (old_group_nr == 0)
8807         return;
8808
8809       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8810       AmoebaCnt[old_group_nr] = 0;
8811       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8812       AmoebaCnt2[old_group_nr] = 0;
8813
8814       SCAN_PLAYFIELD(xx, yy)
8815       {
8816         if (AmoebaNr[xx][yy] == old_group_nr)
8817           AmoebaNr[xx][yy] = new_group_nr;
8818       }
8819     }
8820   }
8821 }
8822
8823 void AmoebeUmwandeln(int ax, int ay)
8824 {
8825   int i, x, y;
8826
8827   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8828   {
8829     int group_nr = AmoebaNr[ax][ay];
8830
8831 #ifdef DEBUG
8832     if (group_nr == 0)
8833     {
8834       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8835       printf("AmoebeUmwandeln(): This should never happen!\n");
8836       return;
8837     }
8838 #endif
8839
8840     SCAN_PLAYFIELD(x, y)
8841     {
8842       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8843       {
8844         AmoebaNr[x][y] = 0;
8845         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8846       }
8847     }
8848
8849     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8850                             SND_AMOEBA_TURNING_TO_GEM :
8851                             SND_AMOEBA_TURNING_TO_ROCK));
8852     Bang(ax, ay);
8853   }
8854   else
8855   {
8856     static int xy[4][2] =
8857     {
8858       { 0, -1 },
8859       { -1, 0 },
8860       { +1, 0 },
8861       { 0, +1 }
8862     };
8863
8864     for (i = 0; i < NUM_DIRECTIONS; i++)
8865     {
8866       x = ax + xy[i][0];
8867       y = ay + xy[i][1];
8868
8869       if (!IN_LEV_FIELD(x, y))
8870         continue;
8871
8872       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8873       {
8874         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8875                               SND_AMOEBA_TURNING_TO_GEM :
8876                               SND_AMOEBA_TURNING_TO_ROCK));
8877         Bang(x, y);
8878       }
8879     }
8880   }
8881 }
8882
8883 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8884 {
8885   int x, y;
8886   int group_nr = AmoebaNr[ax][ay];
8887   boolean done = FALSE;
8888
8889 #ifdef DEBUG
8890   if (group_nr == 0)
8891   {
8892     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8893     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8894     return;
8895   }
8896 #endif
8897
8898   SCAN_PLAYFIELD(x, y)
8899   {
8900     if (AmoebaNr[x][y] == group_nr &&
8901         (Feld[x][y] == EL_AMOEBA_DEAD ||
8902          Feld[x][y] == EL_BD_AMOEBA ||
8903          Feld[x][y] == EL_AMOEBA_GROWING))
8904     {
8905       AmoebaNr[x][y] = 0;
8906       Feld[x][y] = new_element;
8907       InitField(x, y, FALSE);
8908       DrawLevelField(x, y);
8909       done = TRUE;
8910     }
8911   }
8912
8913   if (done)
8914     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8915                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8916                             SND_BD_AMOEBA_TURNING_TO_GEM));
8917 }
8918
8919 void AmoebeWaechst(int x, int y)
8920 {
8921   static unsigned long sound_delay = 0;
8922   static unsigned long sound_delay_value = 0;
8923
8924   if (!MovDelay[x][y])          /* start new growing cycle */
8925   {
8926     MovDelay[x][y] = 7;
8927
8928     if (DelayReached(&sound_delay, sound_delay_value))
8929     {
8930       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8931       sound_delay_value = 30;
8932     }
8933   }
8934
8935   if (MovDelay[x][y])           /* wait some time before growing bigger */
8936   {
8937     MovDelay[x][y]--;
8938     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8939     {
8940       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8941                                            6 - MovDelay[x][y]);
8942
8943       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8944     }
8945
8946     if (!MovDelay[x][y])
8947     {
8948       Feld[x][y] = Store[x][y];
8949       Store[x][y] = 0;
8950       DrawLevelField(x, y);
8951     }
8952   }
8953 }
8954
8955 void AmoebaDisappearing(int x, int y)
8956 {
8957   static unsigned long sound_delay = 0;
8958   static unsigned long sound_delay_value = 0;
8959
8960   if (!MovDelay[x][y])          /* start new shrinking cycle */
8961   {
8962     MovDelay[x][y] = 7;
8963
8964     if (DelayReached(&sound_delay, sound_delay_value))
8965       sound_delay_value = 30;
8966   }
8967
8968   if (MovDelay[x][y])           /* wait some time before shrinking */
8969   {
8970     MovDelay[x][y]--;
8971     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8972     {
8973       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8974                                            6 - MovDelay[x][y]);
8975
8976       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8977     }
8978
8979     if (!MovDelay[x][y])
8980     {
8981       Feld[x][y] = EL_EMPTY;
8982       DrawLevelField(x, y);
8983
8984       /* don't let mole enter this field in this cycle;
8985          (give priority to objects falling to this field from above) */
8986       Stop[x][y] = TRUE;
8987     }
8988   }
8989 }
8990
8991 void AmoebeAbleger(int ax, int ay)
8992 {
8993   int i;
8994   int element = Feld[ax][ay];
8995   int graphic = el2img(element);
8996   int newax = ax, neway = ay;
8997   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8998   static int xy[4][2] =
8999   {
9000     { 0, -1 },
9001     { -1, 0 },
9002     { +1, 0 },
9003     { 0, +1 }
9004   };
9005
9006   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9007   {
9008     Feld[ax][ay] = EL_AMOEBA_DEAD;
9009     DrawLevelField(ax, ay);
9010     return;
9011   }
9012
9013   if (IS_ANIMATED(graphic))
9014     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9015
9016   if (!MovDelay[ax][ay])        /* start making new amoeba field */
9017     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9018
9019   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
9020   {
9021     MovDelay[ax][ay]--;
9022     if (MovDelay[ax][ay])
9023       return;
9024   }
9025
9026   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9027   {
9028     int start = RND(4);
9029     int x = ax + xy[start][0];
9030     int y = ay + xy[start][1];
9031
9032     if (!IN_LEV_FIELD(x, y))
9033       return;
9034
9035     if (IS_FREE(x, y) ||
9036         CAN_GROW_INTO(Feld[x][y]) ||
9037         Feld[x][y] == EL_QUICKSAND_EMPTY ||
9038         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9039     {
9040       newax = x;
9041       neway = y;
9042     }
9043
9044     if (newax == ax && neway == ay)
9045       return;
9046   }
9047   else                          /* normal or "filled" (BD style) amoeba */
9048   {
9049     int start = RND(4);
9050     boolean waiting_for_player = FALSE;
9051
9052     for (i = 0; i < NUM_DIRECTIONS; i++)
9053     {
9054       int j = (start + i) % 4;
9055       int x = ax + xy[j][0];
9056       int y = ay + xy[j][1];
9057
9058       if (!IN_LEV_FIELD(x, y))
9059         continue;
9060
9061       if (IS_FREE(x, y) ||
9062           CAN_GROW_INTO(Feld[x][y]) ||
9063           Feld[x][y] == EL_QUICKSAND_EMPTY ||
9064           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9065       {
9066         newax = x;
9067         neway = y;
9068         break;
9069       }
9070       else if (IS_PLAYER(x, y))
9071         waiting_for_player = TRUE;
9072     }
9073
9074     if (newax == ax && neway == ay)             /* amoeba cannot grow */
9075     {
9076       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9077       {
9078         Feld[ax][ay] = EL_AMOEBA_DEAD;
9079         DrawLevelField(ax, ay);
9080         AmoebaCnt[AmoebaNr[ax][ay]]--;
9081
9082         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
9083         {
9084           if (element == EL_AMOEBA_FULL)
9085             AmoebeUmwandeln(ax, ay);
9086           else if (element == EL_BD_AMOEBA)
9087             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9088         }
9089       }
9090       return;
9091     }
9092     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9093     {
9094       /* amoeba gets larger by growing in some direction */
9095
9096       int new_group_nr = AmoebaNr[ax][ay];
9097
9098 #ifdef DEBUG
9099   if (new_group_nr == 0)
9100   {
9101     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9102     printf("AmoebeAbleger(): This should never happen!\n");
9103     return;
9104   }
9105 #endif
9106
9107       AmoebaNr[newax][neway] = new_group_nr;
9108       AmoebaCnt[new_group_nr]++;
9109       AmoebaCnt2[new_group_nr]++;
9110
9111       /* if amoeba touches other amoeba(s) after growing, unify them */
9112       AmoebenVereinigen(newax, neway);
9113
9114       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9115       {
9116         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9117         return;
9118       }
9119     }
9120   }
9121
9122   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9123       (neway == lev_fieldy - 1 && newax != ax))
9124   {
9125     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
9126     Store[newax][neway] = element;
9127   }
9128   else if (neway == ay || element == EL_EMC_DRIPPER)
9129   {
9130     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
9131
9132     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9133   }
9134   else
9135   {
9136     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
9137     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9138     Store[ax][ay] = EL_AMOEBA_DROP;
9139     ContinueMoving(ax, ay);
9140     return;
9141   }
9142
9143   DrawLevelField(newax, neway);
9144 }
9145
9146 void Life(int ax, int ay)
9147 {
9148   int x1, y1, x2, y2;
9149   int life_time = 40;
9150   int element = Feld[ax][ay];
9151   int graphic = el2img(element);
9152   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9153                          level.biomaze);
9154   boolean changed = FALSE;
9155
9156   if (IS_ANIMATED(graphic))
9157     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9158
9159   if (Stop[ax][ay])
9160     return;
9161
9162   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
9163     MovDelay[ax][ay] = life_time;
9164
9165   if (MovDelay[ax][ay])         /* wait some time before next cycle */
9166   {
9167     MovDelay[ax][ay]--;
9168     if (MovDelay[ax][ay])
9169       return;
9170   }
9171
9172   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9173   {
9174     int xx = ax+x1, yy = ay+y1;
9175     int nachbarn = 0;
9176
9177     if (!IN_LEV_FIELD(xx, yy))
9178       continue;
9179
9180     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9181     {
9182       int x = xx+x2, y = yy+y2;
9183
9184       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9185         continue;
9186
9187       if (((Feld[x][y] == element ||
9188             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9189            !Stop[x][y]) ||
9190           (IS_FREE(x, y) && Stop[x][y]))
9191         nachbarn++;
9192     }
9193
9194     if (xx == ax && yy == ay)           /* field in the middle */
9195     {
9196       if (nachbarn < life_parameter[0] ||
9197           nachbarn > life_parameter[1])
9198       {
9199         Feld[xx][yy] = EL_EMPTY;
9200         if (!Stop[xx][yy])
9201           DrawLevelField(xx, yy);
9202         Stop[xx][yy] = TRUE;
9203         changed = TRUE;
9204       }
9205     }
9206     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9207     {                                   /* free border field */
9208       if (nachbarn >= life_parameter[2] &&
9209           nachbarn <= life_parameter[3])
9210       {
9211         Feld[xx][yy] = element;
9212         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9213         if (!Stop[xx][yy])
9214           DrawLevelField(xx, yy);
9215         Stop[xx][yy] = TRUE;
9216         changed = TRUE;
9217       }
9218     }
9219   }
9220
9221   if (changed)
9222     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9223                    SND_GAME_OF_LIFE_GROWING);
9224 }
9225
9226 static void InitRobotWheel(int x, int y)
9227 {
9228   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9229 }
9230
9231 static void RunRobotWheel(int x, int y)
9232 {
9233   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9234 }
9235
9236 static void StopRobotWheel(int x, int y)
9237 {
9238   if (ZX == x && ZY == y)
9239   {
9240     ZX = ZY = -1;
9241
9242     game.robot_wheel_active = FALSE;
9243   }
9244 }
9245
9246 static void InitTimegateWheel(int x, int y)
9247 {
9248   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9249 }
9250
9251 static void RunTimegateWheel(int x, int y)
9252 {
9253   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9254 }
9255
9256 static void InitMagicBallDelay(int x, int y)
9257 {
9258 #if 1
9259   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9260 #else
9261   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9262 #endif
9263 }
9264
9265 static void ActivateMagicBall(int bx, int by)
9266 {
9267   int x, y;
9268
9269   if (level.ball_random)
9270   {
9271     int pos_border = RND(8);    /* select one of the eight border elements */
9272     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9273     int xx = pos_content % 3;
9274     int yy = pos_content / 3;
9275
9276     x = bx - 1 + xx;
9277     y = by - 1 + yy;
9278
9279     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9280       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9281   }
9282   else
9283   {
9284     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9285     {
9286       int xx = x - bx + 1;
9287       int yy = y - by + 1;
9288
9289       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9290         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9291     }
9292   }
9293
9294   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9295 }
9296
9297 void CheckExit(int x, int y)
9298 {
9299   if (local_player->gems_still_needed > 0 ||
9300       local_player->sokobanfields_still_needed > 0 ||
9301       local_player->lights_still_needed > 0)
9302   {
9303     int element = Feld[x][y];
9304     int graphic = el2img(element);
9305
9306     if (IS_ANIMATED(graphic))
9307       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9308
9309     return;
9310   }
9311
9312   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9313     return;
9314
9315   Feld[x][y] = EL_EXIT_OPENING;
9316
9317   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9318 }
9319
9320 void CheckExitEM(int x, int y)
9321 {
9322   if (local_player->gems_still_needed > 0 ||
9323       local_player->sokobanfields_still_needed > 0 ||
9324       local_player->lights_still_needed > 0)
9325   {
9326     int element = Feld[x][y];
9327     int graphic = el2img(element);
9328
9329     if (IS_ANIMATED(graphic))
9330       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9331
9332     return;
9333   }
9334
9335   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9336     return;
9337
9338   Feld[x][y] = EL_EM_EXIT_OPENING;
9339
9340   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9341 }
9342
9343 void CheckExitSteel(int x, int y)
9344 {
9345   if (local_player->gems_still_needed > 0 ||
9346       local_player->sokobanfields_still_needed > 0 ||
9347       local_player->lights_still_needed > 0)
9348   {
9349     int element = Feld[x][y];
9350     int graphic = el2img(element);
9351
9352     if (IS_ANIMATED(graphic))
9353       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9354
9355     return;
9356   }
9357
9358   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9359     return;
9360
9361   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9362
9363   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9364 }
9365
9366 void CheckExitSteelEM(int x, int y)
9367 {
9368   if (local_player->gems_still_needed > 0 ||
9369       local_player->sokobanfields_still_needed > 0 ||
9370       local_player->lights_still_needed > 0)
9371   {
9372     int element = Feld[x][y];
9373     int graphic = el2img(element);
9374
9375     if (IS_ANIMATED(graphic))
9376       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9377
9378     return;
9379   }
9380
9381   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9382     return;
9383
9384   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9385
9386   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9387 }
9388
9389 void CheckExitSP(int x, int y)
9390 {
9391   if (local_player->gems_still_needed > 0)
9392   {
9393     int element = Feld[x][y];
9394     int graphic = el2img(element);
9395
9396     if (IS_ANIMATED(graphic))
9397       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9398
9399     return;
9400   }
9401
9402   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
9403     return;
9404
9405   Feld[x][y] = EL_SP_EXIT_OPENING;
9406
9407   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9408 }
9409
9410 static void CloseAllOpenTimegates()
9411 {
9412   int x, y;
9413
9414   SCAN_PLAYFIELD(x, y)
9415   {
9416     int element = Feld[x][y];
9417
9418     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9419     {
9420       Feld[x][y] = EL_TIMEGATE_CLOSING;
9421
9422       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9423     }
9424   }
9425 }
9426
9427 void DrawTwinkleOnField(int x, int y)
9428 {
9429   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9430     return;
9431
9432   if (Feld[x][y] == EL_BD_DIAMOND)
9433     return;
9434
9435   if (MovDelay[x][y] == 0)      /* next animation frame */
9436     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9437
9438   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
9439   {
9440     MovDelay[x][y]--;
9441
9442     if (setup.direct_draw && MovDelay[x][y])
9443       SetDrawtoField(DRAW_BUFFERED);
9444
9445     DrawLevelElementAnimation(x, y, Feld[x][y]);
9446
9447     if (MovDelay[x][y] != 0)
9448     {
9449       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9450                                            10 - MovDelay[x][y]);
9451
9452       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9453
9454       if (setup.direct_draw)
9455       {
9456         int dest_x, dest_y;
9457
9458         dest_x = FX + SCREENX(x) * TILEX;
9459         dest_y = FY + SCREENY(y) * TILEY;
9460
9461         BlitBitmap(drawto_field, window,
9462                    dest_x, dest_y, TILEX, TILEY, dest_x, dest_y);
9463         SetDrawtoField(DRAW_DIRECT);
9464       }
9465     }
9466   }
9467 }
9468
9469 void MauerWaechst(int x, int y)
9470 {
9471   int delay = 6;
9472
9473   if (!MovDelay[x][y])          /* next animation frame */
9474     MovDelay[x][y] = 3 * delay;
9475
9476   if (MovDelay[x][y])           /* wait some time before next frame */
9477   {
9478     MovDelay[x][y]--;
9479
9480     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9481     {
9482       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9483       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9484
9485       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9486     }
9487
9488     if (!MovDelay[x][y])
9489     {
9490       if (MovDir[x][y] == MV_LEFT)
9491       {
9492         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9493           DrawLevelField(x - 1, y);
9494       }
9495       else if (MovDir[x][y] == MV_RIGHT)
9496       {
9497         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9498           DrawLevelField(x + 1, y);
9499       }
9500       else if (MovDir[x][y] == MV_UP)
9501       {
9502         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9503           DrawLevelField(x, y - 1);
9504       }
9505       else
9506       {
9507         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9508           DrawLevelField(x, y + 1);
9509       }
9510
9511       Feld[x][y] = Store[x][y];
9512       Store[x][y] = 0;
9513       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9514       DrawLevelField(x, y);
9515     }
9516   }
9517 }
9518
9519 void MauerAbleger(int ax, int ay)
9520 {
9521   int element = Feld[ax][ay];
9522   int graphic = el2img(element);
9523   boolean oben_frei = FALSE, unten_frei = FALSE;
9524   boolean links_frei = FALSE, rechts_frei = FALSE;
9525   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9526   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9527   boolean new_wall = FALSE;
9528
9529   if (IS_ANIMATED(graphic))
9530     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9531
9532   if (!MovDelay[ax][ay])        /* start building new wall */
9533     MovDelay[ax][ay] = 6;
9534
9535   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9536   {
9537     MovDelay[ax][ay]--;
9538     if (MovDelay[ax][ay])
9539       return;
9540   }
9541
9542   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9543     oben_frei = TRUE;
9544   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9545     unten_frei = TRUE;
9546   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9547     links_frei = TRUE;
9548   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9549     rechts_frei = TRUE;
9550
9551   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9552       element == EL_EXPANDABLE_WALL_ANY)
9553   {
9554     if (oben_frei)
9555     {
9556       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9557       Store[ax][ay-1] = element;
9558       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9559       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9560         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9561                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9562       new_wall = TRUE;
9563     }
9564     if (unten_frei)
9565     {
9566       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9567       Store[ax][ay+1] = element;
9568       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9569       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9570         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9571                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9572       new_wall = TRUE;
9573     }
9574   }
9575
9576   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9577       element == EL_EXPANDABLE_WALL_ANY ||
9578       element == EL_EXPANDABLE_WALL ||
9579       element == EL_BD_EXPANDABLE_WALL)
9580   {
9581     if (links_frei)
9582     {
9583       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9584       Store[ax-1][ay] = element;
9585       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9586       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9587         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9588                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9589       new_wall = TRUE;
9590     }
9591
9592     if (rechts_frei)
9593     {
9594       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9595       Store[ax+1][ay] = element;
9596       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9597       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9598         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9599                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9600       new_wall = TRUE;
9601     }
9602   }
9603
9604   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9605     DrawLevelField(ax, ay);
9606
9607   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9608     oben_massiv = TRUE;
9609   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9610     unten_massiv = TRUE;
9611   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9612     links_massiv = TRUE;
9613   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9614     rechts_massiv = TRUE;
9615
9616   if (((oben_massiv && unten_massiv) ||
9617        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9618        element == EL_EXPANDABLE_WALL) &&
9619       ((links_massiv && rechts_massiv) ||
9620        element == EL_EXPANDABLE_WALL_VERTICAL))
9621     Feld[ax][ay] = EL_WALL;
9622
9623   if (new_wall)
9624     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9625 }
9626
9627 void MauerAblegerStahl(int ax, int ay)
9628 {
9629   int element = Feld[ax][ay];
9630   int graphic = el2img(element);
9631   boolean oben_frei = FALSE, unten_frei = FALSE;
9632   boolean links_frei = FALSE, rechts_frei = FALSE;
9633   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9634   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9635   boolean new_wall = FALSE;
9636
9637   if (IS_ANIMATED(graphic))
9638     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9639
9640   if (!MovDelay[ax][ay])        /* start building new wall */
9641     MovDelay[ax][ay] = 6;
9642
9643   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9644   {
9645     MovDelay[ax][ay]--;
9646     if (MovDelay[ax][ay])
9647       return;
9648   }
9649
9650   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9651     oben_frei = TRUE;
9652   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9653     unten_frei = TRUE;
9654   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9655     links_frei = TRUE;
9656   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9657     rechts_frei = TRUE;
9658
9659   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9660       element == EL_EXPANDABLE_STEELWALL_ANY)
9661   {
9662     if (oben_frei)
9663     {
9664       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9665       Store[ax][ay-1] = element;
9666       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9667       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9668         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9669                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9670       new_wall = TRUE;
9671     }
9672     if (unten_frei)
9673     {
9674       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9675       Store[ax][ay+1] = element;
9676       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9677       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9678         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9679                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9680       new_wall = TRUE;
9681     }
9682   }
9683
9684   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9685       element == EL_EXPANDABLE_STEELWALL_ANY)
9686   {
9687     if (links_frei)
9688     {
9689       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9690       Store[ax-1][ay] = element;
9691       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9692       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9693         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9694                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9695       new_wall = TRUE;
9696     }
9697
9698     if (rechts_frei)
9699     {
9700       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9701       Store[ax+1][ay] = element;
9702       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9703       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9704         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9705                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9706       new_wall = TRUE;
9707     }
9708   }
9709
9710   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9711     oben_massiv = TRUE;
9712   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9713     unten_massiv = TRUE;
9714   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9715     links_massiv = TRUE;
9716   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9717     rechts_massiv = TRUE;
9718
9719   if (((oben_massiv && unten_massiv) ||
9720        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9721       ((links_massiv && rechts_massiv) ||
9722        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9723     Feld[ax][ay] = EL_WALL;
9724
9725   if (new_wall)
9726     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9727 }
9728
9729 void CheckForDragon(int x, int y)
9730 {
9731   int i, j;
9732   boolean dragon_found = FALSE;
9733   static int xy[4][2] =
9734   {
9735     { 0, -1 },
9736     { -1, 0 },
9737     { +1, 0 },
9738     { 0, +1 }
9739   };
9740
9741   for (i = 0; i < NUM_DIRECTIONS; i++)
9742   {
9743     for (j = 0; j < 4; j++)
9744     {
9745       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9746
9747       if (IN_LEV_FIELD(xx, yy) &&
9748           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9749       {
9750         if (Feld[xx][yy] == EL_DRAGON)
9751           dragon_found = TRUE;
9752       }
9753       else
9754         break;
9755     }
9756   }
9757
9758   if (!dragon_found)
9759   {
9760     for (i = 0; i < NUM_DIRECTIONS; i++)
9761     {
9762       for (j = 0; j < 3; j++)
9763       {
9764         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9765   
9766         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9767         {
9768           Feld[xx][yy] = EL_EMPTY;
9769           DrawLevelField(xx, yy);
9770         }
9771         else
9772           break;
9773       }
9774     }
9775   }
9776 }
9777
9778 static void InitBuggyBase(int x, int y)
9779 {
9780   int element = Feld[x][y];
9781   int activating_delay = FRAMES_PER_SECOND / 4;
9782
9783   ChangeDelay[x][y] =
9784     (element == EL_SP_BUGGY_BASE ?
9785      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9786      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9787      activating_delay :
9788      element == EL_SP_BUGGY_BASE_ACTIVE ?
9789      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9790 }
9791
9792 static void WarnBuggyBase(int x, int y)
9793 {
9794   int i;
9795   static int xy[4][2] =
9796   {
9797     { 0, -1 },
9798     { -1, 0 },
9799     { +1, 0 },
9800     { 0, +1 }
9801   };
9802
9803   for (i = 0; i < NUM_DIRECTIONS; i++)
9804   {
9805     int xx = x + xy[i][0];
9806     int yy = y + xy[i][1];
9807
9808     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9809     {
9810       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9811
9812       break;
9813     }
9814   }
9815 }
9816
9817 static void InitTrap(int x, int y)
9818 {
9819   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9820 }
9821
9822 static void ActivateTrap(int x, int y)
9823 {
9824   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9825 }
9826
9827 static void ChangeActiveTrap(int x, int y)
9828 {
9829   int graphic = IMG_TRAP_ACTIVE;
9830
9831   /* if new animation frame was drawn, correct crumbled sand border */
9832   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9833     DrawLevelFieldCrumbledSand(x, y);
9834 }
9835
9836 static int getSpecialActionElement(int element, int number, int base_element)
9837 {
9838   return (element != EL_EMPTY ? element :
9839           number != -1 ? base_element + number - 1 :
9840           EL_EMPTY);
9841 }
9842
9843 static int getModifiedActionNumber(int value_old, int operator, int operand,
9844                                    int value_min, int value_max)
9845 {
9846   int value_new = (operator == CA_MODE_SET      ? operand :
9847                    operator == CA_MODE_ADD      ? value_old + operand :
9848                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9849                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9850                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9851                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9852                    value_old);
9853
9854   return (value_new < value_min ? value_min :
9855           value_new > value_max ? value_max :
9856           value_new);
9857 }
9858
9859 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9860 {
9861   struct ElementInfo *ei = &element_info[element];
9862   struct ElementChangeInfo *change = &ei->change_page[page];
9863   int target_element = change->target_element;
9864   int action_type = change->action_type;
9865   int action_mode = change->action_mode;
9866   int action_arg = change->action_arg;
9867   int i;
9868
9869   if (!change->has_action)
9870     return;
9871
9872   /* ---------- determine action paramater values -------------------------- */
9873
9874   int level_time_value =
9875     (level.time > 0 ? TimeLeft :
9876      TimePlayed);
9877
9878   int action_arg_element =
9879     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9880      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9881      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9882      EL_EMPTY);
9883
9884   int action_arg_direction =
9885     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9886      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9887      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9888      change->actual_trigger_side :
9889      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9890      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9891      MV_NONE);
9892
9893   int action_arg_number_min =
9894     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9895      CA_ARG_MIN);
9896
9897   int action_arg_number_max =
9898     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9899      action_type == CA_SET_LEVEL_GEMS ? 999 :
9900      action_type == CA_SET_LEVEL_TIME ? 9999 :
9901      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9902      action_type == CA_SET_CE_VALUE ? 9999 :
9903      action_type == CA_SET_CE_SCORE ? 9999 :
9904      CA_ARG_MAX);
9905
9906   int action_arg_number_reset =
9907     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9908      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9909      action_type == CA_SET_LEVEL_TIME ? level.time :
9910      action_type == CA_SET_LEVEL_SCORE ? 0 :
9911 #if USE_NEW_CUSTOM_VALUE
9912      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9913 #else
9914      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
9915 #endif
9916      action_type == CA_SET_CE_SCORE ? 0 :
9917      0);
9918
9919   int action_arg_number =
9920     (action_arg <= CA_ARG_MAX ? action_arg :
9921      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9922      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9923      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9924      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9925      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9926      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9927 #if USE_NEW_CUSTOM_VALUE
9928      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9929 #else
9930      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
9931 #endif
9932      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9933      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9934      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9935      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9936      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9937      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9938      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9939      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9940      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9941      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9942      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9943      -1);
9944
9945   int action_arg_number_old =
9946     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9947      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9948      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9949      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9950      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9951      0);
9952
9953   int action_arg_number_new =
9954     getModifiedActionNumber(action_arg_number_old,
9955                             action_mode, action_arg_number,
9956                             action_arg_number_min, action_arg_number_max);
9957
9958   int trigger_player_bits =
9959     (change->actual_trigger_player >= EL_PLAYER_1 &&
9960      change->actual_trigger_player <= EL_PLAYER_4 ?
9961      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
9962      PLAYER_BITS_ANY);
9963
9964   int action_arg_player_bits =
9965     (action_arg >= CA_ARG_PLAYER_1 &&
9966      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9967      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9968      PLAYER_BITS_ANY);
9969
9970   /* ---------- execute action  -------------------------------------------- */
9971
9972   switch (action_type)
9973   {
9974     case CA_NO_ACTION:
9975     {
9976       return;
9977     }
9978
9979     /* ---------- level actions  ------------------------------------------- */
9980
9981     case CA_RESTART_LEVEL:
9982     {
9983       game.restart_level = TRUE;
9984
9985       break;
9986     }
9987
9988     case CA_SHOW_ENVELOPE:
9989     {
9990       int element = getSpecialActionElement(action_arg_element,
9991                                             action_arg_number, EL_ENVELOPE_1);
9992
9993       if (IS_ENVELOPE(element))
9994         local_player->show_envelope = element;
9995
9996       break;
9997     }
9998
9999     case CA_SET_LEVEL_TIME:
10000     {
10001       if (level.time > 0)       /* only modify limited time value */
10002       {
10003         TimeLeft = action_arg_number_new;
10004
10005 #if 1
10006         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10007
10008         DisplayGameControlValues();
10009 #else
10010         DrawGameValue_Time(TimeLeft);
10011 #endif
10012
10013         if (!TimeLeft && setup.time_limit)
10014           for (i = 0; i < MAX_PLAYERS; i++)
10015             KillPlayer(&stored_player[i]);
10016       }
10017
10018       break;
10019     }
10020
10021     case CA_SET_LEVEL_SCORE:
10022     {
10023       local_player->score = action_arg_number_new;
10024
10025 #if 1
10026       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10027
10028       DisplayGameControlValues();
10029 #else
10030       DrawGameValue_Score(local_player->score);
10031 #endif
10032
10033       break;
10034     }
10035
10036     case CA_SET_LEVEL_GEMS:
10037     {
10038       local_player->gems_still_needed = action_arg_number_new;
10039
10040 #if 1
10041       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10042
10043       DisplayGameControlValues();
10044 #else
10045       DrawGameValue_Emeralds(local_player->gems_still_needed);
10046 #endif
10047
10048       break;
10049     }
10050
10051 #if !USE_PLAYER_GRAVITY
10052     case CA_SET_LEVEL_GRAVITY:
10053     {
10054       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10055                       action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10056                       action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10057                       game.gravity);
10058       break;
10059     }
10060 #endif
10061
10062     case CA_SET_LEVEL_WIND:
10063     {
10064       game.wind_direction = action_arg_direction;
10065
10066       break;
10067     }
10068
10069     /* ---------- player actions  ------------------------------------------ */
10070
10071     case CA_MOVE_PLAYER:
10072     {
10073       /* automatically move to the next field in specified direction */
10074       for (i = 0; i < MAX_PLAYERS; i++)
10075         if (trigger_player_bits & (1 << i))
10076           stored_player[i].programmed_action = action_arg_direction;
10077
10078       break;
10079     }
10080
10081     case CA_EXIT_PLAYER:
10082     {
10083       for (i = 0; i < MAX_PLAYERS; i++)
10084         if (action_arg_player_bits & (1 << i))
10085           PlayerWins(&stored_player[i]);
10086
10087       break;
10088     }
10089
10090     case CA_KILL_PLAYER:
10091     {
10092       for (i = 0; i < MAX_PLAYERS; i++)
10093         if (action_arg_player_bits & (1 << i))
10094           KillPlayer(&stored_player[i]);
10095
10096       break;
10097     }
10098
10099     case CA_SET_PLAYER_KEYS:
10100     {
10101       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10102       int element = getSpecialActionElement(action_arg_element,
10103                                             action_arg_number, EL_KEY_1);
10104
10105       if (IS_KEY(element))
10106       {
10107         for (i = 0; i < MAX_PLAYERS; i++)
10108         {
10109           if (trigger_player_bits & (1 << i))
10110           {
10111             stored_player[i].key[KEY_NR(element)] = key_state;
10112
10113             DrawGameDoorValues();
10114           }
10115         }
10116       }
10117
10118       break;
10119     }
10120
10121     case CA_SET_PLAYER_SPEED:
10122     {
10123       for (i = 0; i < MAX_PLAYERS; i++)
10124       {
10125         if (trigger_player_bits & (1 << i))
10126         {
10127           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10128
10129           if (action_arg == CA_ARG_SPEED_FASTER &&
10130               stored_player[i].cannot_move)
10131           {
10132             action_arg_number = STEPSIZE_VERY_SLOW;
10133           }
10134           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10135                    action_arg == CA_ARG_SPEED_FASTER)
10136           {
10137             action_arg_number = 2;
10138             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10139                            CA_MODE_MULTIPLY);
10140           }
10141           else if (action_arg == CA_ARG_NUMBER_RESET)
10142           {
10143             action_arg_number = level.initial_player_stepsize[i];
10144           }
10145
10146           move_stepsize =
10147             getModifiedActionNumber(move_stepsize,
10148                                     action_mode,
10149                                     action_arg_number,
10150                                     action_arg_number_min,
10151                                     action_arg_number_max);
10152
10153           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10154         }
10155       }
10156
10157       break;
10158     }
10159
10160     case CA_SET_PLAYER_SHIELD:
10161     {
10162       for (i = 0; i < MAX_PLAYERS; i++)
10163       {
10164         if (trigger_player_bits & (1 << i))
10165         {
10166           if (action_arg == CA_ARG_SHIELD_OFF)
10167           {
10168             stored_player[i].shield_normal_time_left = 0;
10169             stored_player[i].shield_deadly_time_left = 0;
10170           }
10171           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10172           {
10173             stored_player[i].shield_normal_time_left = 999999;
10174           }
10175           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10176           {
10177             stored_player[i].shield_normal_time_left = 999999;
10178             stored_player[i].shield_deadly_time_left = 999999;
10179           }
10180         }
10181       }
10182
10183       break;
10184     }
10185
10186 #if USE_PLAYER_GRAVITY
10187     case CA_SET_PLAYER_GRAVITY:
10188     {
10189       for (i = 0; i < MAX_PLAYERS; i++)
10190       {
10191         if (trigger_player_bits & (1 << i))
10192         {
10193           stored_player[i].gravity =
10194             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10195              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10196              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10197              stored_player[i].gravity);
10198         }
10199       }
10200
10201       break;
10202     }
10203 #endif
10204
10205     case CA_SET_PLAYER_ARTWORK:
10206     {
10207       for (i = 0; i < MAX_PLAYERS; i++)
10208       {
10209         if (trigger_player_bits & (1 << i))
10210         {
10211           int artwork_element = action_arg_element;
10212
10213           if (action_arg == CA_ARG_ELEMENT_RESET)
10214             artwork_element =
10215               (level.use_artwork_element[i] ? level.artwork_element[i] :
10216                stored_player[i].element_nr);
10217
10218 #if USE_GFX_RESET_PLAYER_ARTWORK
10219           if (stored_player[i].artwork_element != artwork_element)
10220             stored_player[i].Frame = 0;
10221 #endif
10222
10223           stored_player[i].artwork_element = artwork_element;
10224
10225           SetPlayerWaiting(&stored_player[i], FALSE);
10226
10227           /* set number of special actions for bored and sleeping animation */
10228           stored_player[i].num_special_action_bored =
10229             get_num_special_action(artwork_element,
10230                                    ACTION_BORING_1, ACTION_BORING_LAST);
10231           stored_player[i].num_special_action_sleeping =
10232             get_num_special_action(artwork_element,
10233                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10234         }
10235       }
10236
10237       break;
10238     }
10239
10240     /* ---------- CE actions  ---------------------------------------------- */
10241
10242     case CA_SET_CE_VALUE:
10243     {
10244 #if USE_NEW_CUSTOM_VALUE
10245       int last_ce_value = CustomValue[x][y];
10246
10247       CustomValue[x][y] = action_arg_number_new;
10248
10249       if (CustomValue[x][y] != last_ce_value)
10250       {
10251         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10252         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10253
10254         if (CustomValue[x][y] == 0)
10255         {
10256           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10257           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10258         }
10259       }
10260 #endif
10261
10262       break;
10263     }
10264
10265     case CA_SET_CE_SCORE:
10266     {
10267 #if USE_NEW_CUSTOM_VALUE
10268       int last_ce_score = ei->collect_score;
10269
10270       ei->collect_score = action_arg_number_new;
10271
10272       if (ei->collect_score != last_ce_score)
10273       {
10274         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10275         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10276
10277         if (ei->collect_score == 0)
10278         {
10279           int xx, yy;
10280
10281           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10282           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10283
10284           /*
10285             This is a very special case that seems to be a mixture between
10286             CheckElementChange() and CheckTriggeredElementChange(): while
10287             the first one only affects single elements that are triggered
10288             directly, the second one affects multiple elements in the playfield
10289             that are triggered indirectly by another element. This is a third
10290             case: Changing the CE score always affects multiple identical CEs,
10291             so every affected CE must be checked, not only the single CE for
10292             which the CE score was changed in the first place (as every instance
10293             of that CE shares the same CE score, and therefore also can change)!
10294           */
10295           SCAN_PLAYFIELD(xx, yy)
10296           {
10297             if (Feld[xx][yy] == element)
10298               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10299                                  CE_SCORE_GETS_ZERO);
10300           }
10301         }
10302       }
10303 #endif
10304
10305       break;
10306     }
10307
10308     /* ---------- engine actions  ------------------------------------------ */
10309
10310     case CA_SET_ENGINE_SCAN_MODE:
10311     {
10312       InitPlayfieldScanMode(action_arg);
10313
10314       break;
10315     }
10316
10317     default:
10318       break;
10319   }
10320 }
10321
10322 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10323 {
10324   int old_element = Feld[x][y];
10325   int new_element = GetElementFromGroupElement(element);
10326   int previous_move_direction = MovDir[x][y];
10327 #if USE_NEW_CUSTOM_VALUE
10328   int last_ce_value = CustomValue[x][y];
10329 #endif
10330   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10331   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10332   boolean add_player_onto_element = (new_element_is_player &&
10333 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10334                                      /* this breaks SnakeBite when a snake is
10335                                         halfway through a door that closes */
10336                                      /* NOW FIXED AT LEVEL INIT IN files.c */
10337                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10338 #endif
10339                                      IS_WALKABLE(old_element));
10340
10341 #if 0
10342   /* check if element under the player changes from accessible to unaccessible
10343      (needed for special case of dropping element which then changes) */
10344   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10345       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10346   {
10347     Bang(x, y);
10348
10349     return;
10350   }
10351 #endif
10352
10353   if (!add_player_onto_element)
10354   {
10355     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10356       RemoveMovingField(x, y);
10357     else
10358       RemoveField(x, y);
10359
10360     Feld[x][y] = new_element;
10361
10362 #if !USE_GFX_RESET_GFX_ANIMATION
10363     ResetGfxAnimation(x, y);
10364     ResetRandomAnimationValue(x, y);
10365 #endif
10366
10367     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10368       MovDir[x][y] = previous_move_direction;
10369
10370 #if USE_NEW_CUSTOM_VALUE
10371     if (element_info[new_element].use_last_ce_value)
10372       CustomValue[x][y] = last_ce_value;
10373 #endif
10374
10375     InitField_WithBug1(x, y, FALSE);
10376
10377     new_element = Feld[x][y];   /* element may have changed */
10378
10379 #if USE_GFX_RESET_GFX_ANIMATION
10380     ResetGfxAnimation(x, y);
10381     ResetRandomAnimationValue(x, y);
10382 #endif
10383
10384     DrawLevelField(x, y);
10385
10386     if (GFX_CRUMBLED(new_element))
10387       DrawLevelFieldCrumbledSandNeighbours(x, y);
10388   }
10389
10390 #if 1
10391   /* check if element under the player changes from accessible to unaccessible
10392      (needed for special case of dropping element which then changes) */
10393   /* (must be checked after creating new element for walkable group elements) */
10394 #if USE_FIX_KILLED_BY_NON_WALKABLE
10395   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10396       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10397   {
10398     Bang(x, y);
10399
10400     return;
10401   }
10402 #else
10403   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10404       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10405   {
10406     Bang(x, y);
10407
10408     return;
10409   }
10410 #endif
10411 #endif
10412
10413   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10414   if (new_element_is_player)
10415     RelocatePlayer(x, y, new_element);
10416
10417   if (is_change)
10418     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10419
10420   TestIfBadThingTouchesPlayer(x, y);
10421   TestIfPlayerTouchesCustomElement(x, y);
10422   TestIfElementTouchesCustomElement(x, y);
10423 }
10424
10425 static void CreateField(int x, int y, int element)
10426 {
10427   CreateFieldExt(x, y, element, FALSE);
10428 }
10429
10430 static void CreateElementFromChange(int x, int y, int element)
10431 {
10432   element = GET_VALID_RUNTIME_ELEMENT(element);
10433
10434 #if USE_STOP_CHANGED_ELEMENTS
10435   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10436   {
10437     int old_element = Feld[x][y];
10438
10439     /* prevent changed element from moving in same engine frame
10440        unless both old and new element can either fall or move */
10441     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10442         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10443       Stop[x][y] = TRUE;
10444   }
10445 #endif
10446
10447   CreateFieldExt(x, y, element, TRUE);
10448 }
10449
10450 static boolean ChangeElement(int x, int y, int element, int page)
10451 {
10452   struct ElementInfo *ei = &element_info[element];
10453   struct ElementChangeInfo *change = &ei->change_page[page];
10454   int ce_value = CustomValue[x][y];
10455   int ce_score = ei->collect_score;
10456   int target_element;
10457   int old_element = Feld[x][y];
10458
10459   /* always use default change event to prevent running into a loop */
10460   if (ChangeEvent[x][y] == -1)
10461     ChangeEvent[x][y] = CE_DELAY;
10462
10463   if (ChangeEvent[x][y] == CE_DELAY)
10464   {
10465     /* reset actual trigger element, trigger player and action element */
10466     change->actual_trigger_element = EL_EMPTY;
10467     change->actual_trigger_player = EL_PLAYER_1;
10468     change->actual_trigger_side = CH_SIDE_NONE;
10469     change->actual_trigger_ce_value = 0;
10470     change->actual_trigger_ce_score = 0;
10471   }
10472
10473   /* do not change elements more than a specified maximum number of changes */
10474   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10475     return FALSE;
10476
10477   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10478
10479   if (change->explode)
10480   {
10481     Bang(x, y);
10482
10483     return TRUE;
10484   }
10485
10486   if (change->use_target_content)
10487   {
10488     boolean complete_replace = TRUE;
10489     boolean can_replace[3][3];
10490     int xx, yy;
10491
10492     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10493     {
10494       boolean is_empty;
10495       boolean is_walkable;
10496       boolean is_diggable;
10497       boolean is_collectible;
10498       boolean is_removable;
10499       boolean is_destructible;
10500       int ex = x + xx - 1;
10501       int ey = y + yy - 1;
10502       int content_element = change->target_content.e[xx][yy];
10503       int e;
10504
10505       can_replace[xx][yy] = TRUE;
10506
10507       if (ex == x && ey == y)   /* do not check changing element itself */
10508         continue;
10509
10510       if (content_element == EL_EMPTY_SPACE)
10511       {
10512         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10513
10514         continue;
10515       }
10516
10517       if (!IN_LEV_FIELD(ex, ey))
10518       {
10519         can_replace[xx][yy] = FALSE;
10520         complete_replace = FALSE;
10521
10522         continue;
10523       }
10524
10525       e = Feld[ex][ey];
10526
10527       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10528         e = MovingOrBlocked2Element(ex, ey);
10529
10530       is_empty = (IS_FREE(ex, ey) ||
10531                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10532
10533       is_walkable     = (is_empty || IS_WALKABLE(e));
10534       is_diggable     = (is_empty || IS_DIGGABLE(e));
10535       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10536       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10537       is_removable    = (is_diggable || is_collectible);
10538
10539       can_replace[xx][yy] =
10540         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10541           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10542           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10543           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10544           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10545           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10546          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10547
10548       if (!can_replace[xx][yy])
10549         complete_replace = FALSE;
10550     }
10551
10552     if (!change->only_if_complete || complete_replace)
10553     {
10554       boolean something_has_changed = FALSE;
10555
10556       if (change->only_if_complete && change->use_random_replace &&
10557           RND(100) < change->random_percentage)
10558         return FALSE;
10559
10560       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10561       {
10562         int ex = x + xx - 1;
10563         int ey = y + yy - 1;
10564         int content_element;
10565
10566         if (can_replace[xx][yy] && (!change->use_random_replace ||
10567                                     RND(100) < change->random_percentage))
10568         {
10569           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10570             RemoveMovingField(ex, ey);
10571
10572           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10573
10574           content_element = change->target_content.e[xx][yy];
10575           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10576                                               ce_value, ce_score);
10577
10578           CreateElementFromChange(ex, ey, target_element);
10579
10580           something_has_changed = TRUE;
10581
10582           /* for symmetry reasons, freeze newly created border elements */
10583           if (ex != x || ey != y)
10584             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10585         }
10586       }
10587
10588       if (something_has_changed)
10589       {
10590         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10591         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10592       }
10593     }
10594   }
10595   else
10596   {
10597     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10598                                         ce_value, ce_score);
10599
10600     if (element == EL_DIAGONAL_GROWING ||
10601         element == EL_DIAGONAL_SHRINKING)
10602     {
10603       target_element = Store[x][y];
10604
10605       Store[x][y] = EL_EMPTY;
10606     }
10607
10608     CreateElementFromChange(x, y, target_element);
10609
10610     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10611     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10612   }
10613
10614   /* this uses direct change before indirect change */
10615   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10616
10617   return TRUE;
10618 }
10619
10620 #if USE_NEW_DELAYED_ACTION
10621
10622 static void HandleElementChange(int x, int y, int page)
10623 {
10624   int element = MovingOrBlocked2Element(x, y);
10625   struct ElementInfo *ei = &element_info[element];
10626   struct ElementChangeInfo *change = &ei->change_page[page];
10627
10628 #ifdef DEBUG
10629   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10630       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10631   {
10632     printf("\n\n");
10633     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10634            x, y, element, element_info[element].token_name);
10635     printf("HandleElementChange(): This should never happen!\n");
10636     printf("\n\n");
10637   }
10638 #endif
10639
10640   /* this can happen with classic bombs on walkable, changing elements */
10641   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10642   {
10643 #if 0
10644     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10645       ChangeDelay[x][y] = 0;
10646 #endif
10647
10648     return;
10649   }
10650
10651   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10652   {
10653     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10654
10655     if (change->can_change)
10656     {
10657 #if 1
10658       /* !!! not clear why graphic animation should be reset at all here !!! */
10659       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10660 #if USE_GFX_RESET_WHEN_NOT_MOVING
10661       /* when a custom element is about to change (for example by change delay),
10662          do not reset graphic animation when the custom element is moving */
10663       if (!IS_MOVING(x, y))
10664 #endif
10665       {
10666         ResetGfxAnimation(x, y);
10667         ResetRandomAnimationValue(x, y);
10668       }
10669 #endif
10670
10671       if (change->pre_change_function)
10672         change->pre_change_function(x, y);
10673     }
10674   }
10675
10676   ChangeDelay[x][y]--;
10677
10678   if (ChangeDelay[x][y] != 0)           /* continue element change */
10679   {
10680     if (change->can_change)
10681     {
10682       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10683
10684       if (IS_ANIMATED(graphic))
10685         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10686
10687       if (change->change_function)
10688         change->change_function(x, y);
10689     }
10690   }
10691   else                                  /* finish element change */
10692   {
10693     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10694     {
10695       page = ChangePage[x][y];
10696       ChangePage[x][y] = -1;
10697
10698       change = &ei->change_page[page];
10699     }
10700
10701     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10702     {
10703       ChangeDelay[x][y] = 1;            /* try change after next move step */
10704       ChangePage[x][y] = page;          /* remember page to use for change */
10705
10706       return;
10707     }
10708
10709     if (change->can_change)
10710     {
10711       if (ChangeElement(x, y, element, page))
10712       {
10713         if (change->post_change_function)
10714           change->post_change_function(x, y);
10715       }
10716     }
10717
10718     if (change->has_action)
10719       ExecuteCustomElementAction(x, y, element, page);
10720   }
10721 }
10722
10723 #else
10724
10725 static void HandleElementChange(int x, int y, int page)
10726 {
10727   int element = MovingOrBlocked2Element(x, y);
10728   struct ElementInfo *ei = &element_info[element];
10729   struct ElementChangeInfo *change = &ei->change_page[page];
10730
10731 #ifdef DEBUG
10732   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
10733   {
10734     printf("\n\n");
10735     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10736            x, y, element, element_info[element].token_name);
10737     printf("HandleElementChange(): This should never happen!\n");
10738     printf("\n\n");
10739   }
10740 #endif
10741
10742   /* this can happen with classic bombs on walkable, changing elements */
10743   if (!CAN_CHANGE(element))
10744   {
10745 #if 0
10746     if (!CAN_CHANGE(Back[x][y]))        /* prevent permanent repetition */
10747       ChangeDelay[x][y] = 0;
10748 #endif
10749
10750     return;
10751   }
10752
10753   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10754   {
10755     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10756
10757     ResetGfxAnimation(x, y);
10758     ResetRandomAnimationValue(x, y);
10759
10760     if (change->pre_change_function)
10761       change->pre_change_function(x, y);
10762   }
10763
10764   ChangeDelay[x][y]--;
10765
10766   if (ChangeDelay[x][y] != 0)           /* continue element change */
10767   {
10768     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10769
10770     if (IS_ANIMATED(graphic))
10771       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10772
10773     if (change->change_function)
10774       change->change_function(x, y);
10775   }
10776   else                                  /* finish element change */
10777   {
10778     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10779     {
10780       page = ChangePage[x][y];
10781       ChangePage[x][y] = -1;
10782
10783       change = &ei->change_page[page];
10784     }
10785
10786     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10787     {
10788       ChangeDelay[x][y] = 1;            /* try change after next move step */
10789       ChangePage[x][y] = page;          /* remember page to use for change */
10790
10791       return;
10792     }
10793
10794     if (ChangeElement(x, y, element, page))
10795     {
10796       if (change->post_change_function)
10797         change->post_change_function(x, y);
10798     }
10799   }
10800 }
10801
10802 #endif
10803
10804 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10805                                               int trigger_element,
10806                                               int trigger_event,
10807                                               int trigger_player,
10808                                               int trigger_side,
10809                                               int trigger_page)
10810 {
10811   boolean change_done_any = FALSE;
10812   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10813   int i;
10814
10815   if (!(trigger_events[trigger_element][trigger_event]))
10816     return FALSE;
10817
10818 #if 0
10819   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10820          trigger_event, recursion_loop_depth, recursion_loop_detected,
10821          recursion_loop_element, EL_NAME(recursion_loop_element));
10822 #endif
10823
10824   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10825
10826   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10827   {
10828     int element = EL_CUSTOM_START + i;
10829     boolean change_done = FALSE;
10830     int p;
10831
10832     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10833         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10834       continue;
10835
10836     for (p = 0; p < element_info[element].num_change_pages; p++)
10837     {
10838       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10839
10840       if (change->can_change_or_has_action &&
10841           change->has_event[trigger_event] &&
10842           change->trigger_side & trigger_side &&
10843           change->trigger_player & trigger_player &&
10844           change->trigger_page & trigger_page_bits &&
10845           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10846       {
10847         change->actual_trigger_element = trigger_element;
10848         change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10849         change->actual_trigger_side = trigger_side;
10850         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10851         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10852
10853         if ((change->can_change && !change_done) || change->has_action)
10854         {
10855           int x, y;
10856
10857           SCAN_PLAYFIELD(x, y)
10858           {
10859             if (Feld[x][y] == element)
10860             {
10861               if (change->can_change && !change_done)
10862               {
10863                 ChangeDelay[x][y] = 1;
10864                 ChangeEvent[x][y] = trigger_event;
10865
10866                 HandleElementChange(x, y, p);
10867               }
10868 #if USE_NEW_DELAYED_ACTION
10869               else if (change->has_action)
10870               {
10871                 ExecuteCustomElementAction(x, y, element, p);
10872                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10873               }
10874 #else
10875               if (change->has_action)
10876               {
10877                 ExecuteCustomElementAction(x, y, element, p);
10878                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10879               }
10880 #endif
10881             }
10882           }
10883
10884           if (change->can_change)
10885           {
10886             change_done = TRUE;
10887             change_done_any = TRUE;
10888           }
10889         }
10890       }
10891     }
10892   }
10893
10894   RECURSION_LOOP_DETECTION_END();
10895
10896   return change_done_any;
10897 }
10898
10899 static boolean CheckElementChangeExt(int x, int y,
10900                                      int element,
10901                                      int trigger_element,
10902                                      int trigger_event,
10903                                      int trigger_player,
10904                                      int trigger_side)
10905 {
10906   boolean change_done = FALSE;
10907   int p;
10908
10909   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10910       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10911     return FALSE;
10912
10913   if (Feld[x][y] == EL_BLOCKED)
10914   {
10915     Blocked2Moving(x, y, &x, &y);
10916     element = Feld[x][y];
10917   }
10918
10919 #if 0
10920   /* check if element has already changed */
10921   if (Feld[x][y] != element)
10922     return FALSE;
10923 #else
10924   /* check if element has already changed or is about to change after moving */
10925   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10926        Feld[x][y] != element) ||
10927
10928       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10929        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10930         ChangePage[x][y] != -1)))
10931     return FALSE;
10932 #endif
10933
10934 #if 0
10935   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
10936          trigger_event, recursion_loop_depth, recursion_loop_detected,
10937          recursion_loop_element, EL_NAME(recursion_loop_element));
10938 #endif
10939
10940   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10941
10942   for (p = 0; p < element_info[element].num_change_pages; p++)
10943   {
10944     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10945
10946     /* check trigger element for all events where the element that is checked
10947        for changing interacts with a directly adjacent element -- this is
10948        different to element changes that affect other elements to change on the
10949        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10950     boolean check_trigger_element =
10951       (trigger_event == CE_TOUCHING_X ||
10952        trigger_event == CE_HITTING_X ||
10953        trigger_event == CE_HIT_BY_X ||
10954 #if 1
10955        /* this one was forgotten until 3.2.3 */
10956        trigger_event == CE_DIGGING_X);
10957 #endif
10958
10959     if (change->can_change_or_has_action &&
10960         change->has_event[trigger_event] &&
10961         change->trigger_side & trigger_side &&
10962         change->trigger_player & trigger_player &&
10963         (!check_trigger_element ||
10964          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10965     {
10966       change->actual_trigger_element = trigger_element;
10967       change->actual_trigger_player = EL_PLAYER_1 + log_2(trigger_player);
10968       change->actual_trigger_side = trigger_side;
10969       change->actual_trigger_ce_value = CustomValue[x][y];
10970       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10971
10972       /* special case: trigger element not at (x,y) position for some events */
10973       if (check_trigger_element)
10974       {
10975         static struct
10976         {
10977           int dx, dy;
10978         } move_xy[] =
10979           {
10980             {  0,  0 },
10981             { -1,  0 },
10982             { +1,  0 },
10983             {  0,  0 },
10984             {  0, -1 },
10985             {  0,  0 }, { 0, 0 }, { 0, 0 },
10986             {  0, +1 }
10987           };
10988
10989         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10990         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10991
10992         change->actual_trigger_ce_value = CustomValue[xx][yy];
10993         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10994       }
10995
10996       if (change->can_change && !change_done)
10997       {
10998         ChangeDelay[x][y] = 1;
10999         ChangeEvent[x][y] = trigger_event;
11000
11001         HandleElementChange(x, y, p);
11002
11003         change_done = TRUE;
11004       }
11005 #if USE_NEW_DELAYED_ACTION
11006       else if (change->has_action)
11007       {
11008         ExecuteCustomElementAction(x, y, element, p);
11009         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11010       }
11011 #else
11012       if (change->has_action)
11013       {
11014         ExecuteCustomElementAction(x, y, element, p);
11015         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11016       }
11017 #endif
11018     }
11019   }
11020
11021   RECURSION_LOOP_DETECTION_END();
11022
11023   return change_done;
11024 }
11025
11026 static void PlayPlayerSound(struct PlayerInfo *player)
11027 {
11028   int jx = player->jx, jy = player->jy;
11029   int sound_element = player->artwork_element;
11030   int last_action = player->last_action_waiting;
11031   int action = player->action_waiting;
11032
11033   if (player->is_waiting)
11034   {
11035     if (action != last_action)
11036       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11037     else
11038       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11039   }
11040   else
11041   {
11042     if (action != last_action)
11043       StopSound(element_info[sound_element].sound[last_action]);
11044
11045     if (last_action == ACTION_SLEEPING)
11046       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11047   }
11048 }
11049
11050 static void PlayAllPlayersSound()
11051 {
11052   int i;
11053
11054   for (i = 0; i < MAX_PLAYERS; i++)
11055     if (stored_player[i].active)
11056       PlayPlayerSound(&stored_player[i]);
11057 }
11058
11059 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11060 {
11061   boolean last_waiting = player->is_waiting;
11062   int move_dir = player->MovDir;
11063
11064   player->dir_waiting = move_dir;
11065   player->last_action_waiting = player->action_waiting;
11066
11067   if (is_waiting)
11068   {
11069     if (!last_waiting)          /* not waiting -> waiting */
11070     {
11071       player->is_waiting = TRUE;
11072
11073       player->frame_counter_bored =
11074         FrameCounter +
11075         game.player_boring_delay_fixed +
11076         GetSimpleRandom(game.player_boring_delay_random);
11077       player->frame_counter_sleeping =
11078         FrameCounter +
11079         game.player_sleeping_delay_fixed +
11080         GetSimpleRandom(game.player_sleeping_delay_random);
11081
11082       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11083     }
11084
11085     if (game.player_sleeping_delay_fixed +
11086         game.player_sleeping_delay_random > 0 &&
11087         player->anim_delay_counter == 0 &&
11088         player->post_delay_counter == 0 &&
11089         FrameCounter >= player->frame_counter_sleeping)
11090       player->is_sleeping = TRUE;
11091     else if (game.player_boring_delay_fixed +
11092              game.player_boring_delay_random > 0 &&
11093              FrameCounter >= player->frame_counter_bored)
11094       player->is_bored = TRUE;
11095
11096     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11097                               player->is_bored ? ACTION_BORING :
11098                               ACTION_WAITING);
11099
11100     if (player->is_sleeping && player->use_murphy)
11101     {
11102       /* special case for sleeping Murphy when leaning against non-free tile */
11103
11104       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11105           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11106            !IS_MOVING(player->jx - 1, player->jy)))
11107         move_dir = MV_LEFT;
11108       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11109                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11110                 !IS_MOVING(player->jx + 1, player->jy)))
11111         move_dir = MV_RIGHT;
11112       else
11113         player->is_sleeping = FALSE;
11114
11115       player->dir_waiting = move_dir;
11116     }
11117
11118     if (player->is_sleeping)
11119     {
11120       if (player->num_special_action_sleeping > 0)
11121       {
11122         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11123         {
11124           int last_special_action = player->special_action_sleeping;
11125           int num_special_action = player->num_special_action_sleeping;
11126           int special_action =
11127             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11128              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11129              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11130              last_special_action + 1 : ACTION_SLEEPING);
11131           int special_graphic =
11132             el_act_dir2img(player->artwork_element, special_action, move_dir);
11133
11134           player->anim_delay_counter =
11135             graphic_info[special_graphic].anim_delay_fixed +
11136             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11137           player->post_delay_counter =
11138             graphic_info[special_graphic].post_delay_fixed +
11139             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11140
11141           player->special_action_sleeping = special_action;
11142         }
11143
11144         if (player->anim_delay_counter > 0)
11145         {
11146           player->action_waiting = player->special_action_sleeping;
11147           player->anim_delay_counter--;
11148         }
11149         else if (player->post_delay_counter > 0)
11150         {
11151           player->post_delay_counter--;
11152         }
11153       }
11154     }
11155     else if (player->is_bored)
11156     {
11157       if (player->num_special_action_bored > 0)
11158       {
11159         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11160         {
11161           int special_action =
11162             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11163           int special_graphic =
11164             el_act_dir2img(player->artwork_element, special_action, move_dir);
11165
11166           player->anim_delay_counter =
11167             graphic_info[special_graphic].anim_delay_fixed +
11168             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11169           player->post_delay_counter =
11170             graphic_info[special_graphic].post_delay_fixed +
11171             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11172
11173           player->special_action_bored = special_action;
11174         }
11175
11176         if (player->anim_delay_counter > 0)
11177         {
11178           player->action_waiting = player->special_action_bored;
11179           player->anim_delay_counter--;
11180         }
11181         else if (player->post_delay_counter > 0)
11182         {
11183           player->post_delay_counter--;
11184         }
11185       }
11186     }
11187   }
11188   else if (last_waiting)        /* waiting -> not waiting */
11189   {
11190     player->is_waiting = FALSE;
11191     player->is_bored = FALSE;
11192     player->is_sleeping = FALSE;
11193
11194     player->frame_counter_bored = -1;
11195     player->frame_counter_sleeping = -1;
11196
11197     player->anim_delay_counter = 0;
11198     player->post_delay_counter = 0;
11199
11200     player->dir_waiting = player->MovDir;
11201     player->action_waiting = ACTION_DEFAULT;
11202
11203     player->special_action_bored = ACTION_DEFAULT;
11204     player->special_action_sleeping = ACTION_DEFAULT;
11205   }
11206 }
11207
11208 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11209 {
11210   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11211   int left      = player_action & JOY_LEFT;
11212   int right     = player_action & JOY_RIGHT;
11213   int up        = player_action & JOY_UP;
11214   int down      = player_action & JOY_DOWN;
11215   int button1   = player_action & JOY_BUTTON_1;
11216   int button2   = player_action & JOY_BUTTON_2;
11217   int dx        = (left ? -1 : right ? 1 : 0);
11218   int dy        = (up   ? -1 : down  ? 1 : 0);
11219
11220   if (!player->active || tape.pausing)
11221     return 0;
11222
11223   if (player_action)
11224   {
11225     if (button1)
11226       snapped = SnapField(player, dx, dy);
11227     else
11228     {
11229       if (button2)
11230         dropped = DropElement(player);
11231
11232       moved = MovePlayer(player, dx, dy);
11233     }
11234
11235     if (tape.single_step && tape.recording && !tape.pausing)
11236     {
11237       if (button1 || (dropped && !moved))
11238       {
11239         TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11240         SnapField(player, 0, 0);                /* stop snapping */
11241       }
11242     }
11243
11244     SetPlayerWaiting(player, FALSE);
11245
11246     return player_action;
11247   }
11248   else
11249   {
11250     /* no actions for this player (no input at player's configured device) */
11251
11252     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11253     SnapField(player, 0, 0);
11254     CheckGravityMovementWhenNotMoving(player);
11255
11256     if (player->MovPos == 0)
11257       SetPlayerWaiting(player, TRUE);
11258
11259     if (player->MovPos == 0)    /* needed for tape.playing */
11260       player->is_moving = FALSE;
11261
11262     player->is_dropping = FALSE;
11263     player->is_dropping_pressed = FALSE;
11264     player->drop_pressed_delay = 0;
11265
11266     return 0;
11267   }
11268 }
11269
11270 static void CheckLevelTime()
11271 {
11272   int i;
11273
11274   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11275   {
11276     if (level.native_em_level->lev->home == 0)  /* all players at home */
11277     {
11278       PlayerWins(local_player);
11279
11280       AllPlayersGone = TRUE;
11281
11282       level.native_em_level->lev->home = -1;
11283     }
11284
11285     if (level.native_em_level->ply[0]->alive == 0 &&
11286         level.native_em_level->ply[1]->alive == 0 &&
11287         level.native_em_level->ply[2]->alive == 0 &&
11288         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11289       AllPlayersGone = TRUE;
11290   }
11291
11292   if (TimeFrames >= FRAMES_PER_SECOND)
11293   {
11294     TimeFrames = 0;
11295     TapeTime++;
11296
11297     for (i = 0; i < MAX_PLAYERS; i++)
11298     {
11299       struct PlayerInfo *player = &stored_player[i];
11300
11301       if (SHIELD_ON(player))
11302       {
11303         player->shield_normal_time_left--;
11304
11305         if (player->shield_deadly_time_left > 0)
11306           player->shield_deadly_time_left--;
11307       }
11308     }
11309
11310     if (!local_player->LevelSolved && !level.use_step_counter)
11311     {
11312       TimePlayed++;
11313
11314       if (TimeLeft > 0)
11315       {
11316         TimeLeft--;
11317
11318         if (TimeLeft <= 10 && setup.time_limit)
11319           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11320
11321 #if 1
11322         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11323
11324         DisplayGameControlValues();
11325 #else
11326         DrawGameValue_Time(TimeLeft);
11327 #endif
11328
11329         if (!TimeLeft && setup.time_limit)
11330         {
11331           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11332             level.native_em_level->lev->killed_out_of_time = TRUE;
11333           else
11334             for (i = 0; i < MAX_PLAYERS; i++)
11335               KillPlayer(&stored_player[i]);
11336         }
11337       }
11338 #if 1
11339       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11340       {
11341         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11342
11343         DisplayGameControlValues();
11344       }
11345 #else
11346       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
11347         DrawGameValue_Time(TimePlayed);
11348 #endif
11349
11350       level.native_em_level->lev->time =
11351         (level.time == 0 ? TimePlayed : TimeLeft);
11352     }
11353
11354     if (tape.recording || tape.playing)
11355       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11356   }
11357
11358   UpdateGameDoorValues();
11359   DrawGameDoorValues();
11360 }
11361
11362 void AdvanceFrameAndPlayerCounters(int player_nr)
11363 {
11364   int i;
11365
11366   /* advance frame counters (global frame counter and time frame counter) */
11367   FrameCounter++;
11368   TimeFrames++;
11369
11370   /* advance player counters (counters for move delay, move animation etc.) */
11371   for (i = 0; i < MAX_PLAYERS; i++)
11372   {
11373     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11374     int move_delay_value = stored_player[i].move_delay_value;
11375     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11376
11377     if (!advance_player_counters)       /* not all players may be affected */
11378       continue;
11379
11380 #if USE_NEW_PLAYER_ANIM
11381     if (move_frames == 0)       /* less than one move per game frame */
11382     {
11383       int stepsize = TILEX / move_delay_value;
11384       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11385       int count = (stored_player[i].is_moving ?
11386                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11387
11388       if (count % delay == 0)
11389         move_frames = 1;
11390     }
11391 #endif
11392
11393     stored_player[i].Frame += move_frames;
11394
11395     if (stored_player[i].MovPos != 0)
11396       stored_player[i].StepFrame += move_frames;
11397
11398     if (stored_player[i].move_delay > 0)
11399       stored_player[i].move_delay--;
11400
11401     /* due to bugs in previous versions, counter must count up, not down */
11402     if (stored_player[i].push_delay != -1)
11403       stored_player[i].push_delay++;
11404
11405     if (stored_player[i].drop_delay > 0)
11406       stored_player[i].drop_delay--;
11407
11408     if (stored_player[i].is_dropping_pressed)
11409       stored_player[i].drop_pressed_delay++;
11410   }
11411 }
11412
11413 void StartGameActions(boolean init_network_game, boolean record_tape,
11414                       long random_seed)
11415 {
11416   unsigned long new_random_seed = InitRND(random_seed);
11417
11418   if (record_tape)
11419     TapeStartRecording(new_random_seed);
11420
11421 #if defined(NETWORK_AVALIABLE)
11422   if (init_network_game)
11423   {
11424     SendToServer_StartPlaying();
11425
11426     return;
11427   }
11428 #endif
11429
11430   InitGame();
11431 }
11432
11433 void GameActions()
11434 {
11435   static unsigned long game_frame_delay = 0;
11436   unsigned long game_frame_delay_value;
11437   byte *recorded_player_action;
11438   byte summarized_player_action = 0;
11439   byte tape_action[MAX_PLAYERS];
11440   int i;
11441
11442   /* detect endless loops, caused by custom element programming */
11443   if (recursion_loop_detected && recursion_loop_depth == 0)
11444   {
11445     char *message = getStringCat3("Internal Error ! Element ",
11446                                   EL_NAME(recursion_loop_element),
11447                                   " caused endless loop ! Quit the game ?");
11448
11449     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11450           EL_NAME(recursion_loop_element));
11451
11452     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11453
11454     recursion_loop_detected = FALSE;    /* if game should be continued */
11455
11456     free(message);
11457
11458     return;
11459   }
11460
11461   if (game.restart_level)
11462     StartGameActions(options.network, setup.autorecord, NEW_RANDOMIZE);
11463
11464   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11465   {
11466     if (level.native_em_level->lev->home == 0)  /* all players at home */
11467     {
11468       PlayerWins(local_player);
11469
11470       AllPlayersGone = TRUE;
11471
11472       level.native_em_level->lev->home = -1;
11473     }
11474
11475     if (level.native_em_level->ply[0]->alive == 0 &&
11476         level.native_em_level->ply[1]->alive == 0 &&
11477         level.native_em_level->ply[2]->alive == 0 &&
11478         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11479       AllPlayersGone = TRUE;
11480   }
11481
11482   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11483     GameWon();
11484
11485   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11486     TapeStop();
11487
11488   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11489     return;
11490
11491   game_frame_delay_value =
11492     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11493
11494   if (tape.playing && tape.warp_forward && !tape.pausing)
11495     game_frame_delay_value = 0;
11496
11497   /* ---------- main game synchronization point ---------- */
11498
11499   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11500
11501   if (network_playing && !network_player_action_received)
11502   {
11503     /* try to get network player actions in time */
11504
11505 #if defined(NETWORK_AVALIABLE)
11506     /* last chance to get network player actions without main loop delay */
11507     HandleNetworking();
11508 #endif
11509
11510     /* game was quit by network peer */
11511     if (game_status != GAME_MODE_PLAYING)
11512       return;
11513
11514     if (!network_player_action_received)
11515       return;           /* failed to get network player actions in time */
11516
11517     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11518   }
11519
11520   if (tape.pausing)
11521     return;
11522
11523   /* at this point we know that we really continue executing the game */
11524
11525   network_player_action_received = FALSE;
11526
11527   /* when playing tape, read previously recorded player input from tape data */
11528   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11529
11530 #if 1
11531   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11532   if (tape.pausing)
11533     return;
11534 #endif
11535
11536   if (tape.set_centered_player)
11537   {
11538     game.centered_player_nr_next = tape.centered_player_nr_next;
11539     game.set_centered_player = TRUE;
11540   }
11541
11542   for (i = 0; i < MAX_PLAYERS; i++)
11543   {
11544     summarized_player_action |= stored_player[i].action;
11545
11546     if (!network_playing)
11547       stored_player[i].effective_action = stored_player[i].action;
11548   }
11549
11550 #if defined(NETWORK_AVALIABLE)
11551   if (network_playing)
11552     SendToServer_MovePlayer(summarized_player_action);
11553 #endif
11554
11555   if (!options.network && !setup.team_mode)
11556     local_player->effective_action = summarized_player_action;
11557
11558   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
11559   {
11560     for (i = 0; i < MAX_PLAYERS; i++)
11561       stored_player[i].effective_action =
11562         (i == game.centered_player_nr ? summarized_player_action : 0);
11563   }
11564
11565   if (recorded_player_action != NULL)
11566     for (i = 0; i < MAX_PLAYERS; i++)
11567       stored_player[i].effective_action = recorded_player_action[i];
11568
11569   for (i = 0; i < MAX_PLAYERS; i++)
11570   {
11571     tape_action[i] = stored_player[i].effective_action;
11572
11573     /* (this can only happen in the R'n'D game engine) */
11574     if (tape.recording && tape_action[i] && !tape.player_participates[i])
11575       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
11576   }
11577
11578   /* only record actions from input devices, but not programmed actions */
11579   if (tape.recording)
11580     TapeRecordAction(tape_action);
11581
11582   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11583   {
11584     GameActions_EM_Main();
11585   }
11586   else
11587   {
11588     GameActions_RND();
11589   }
11590 }
11591
11592 void GameActions_EM_Main()
11593 {
11594   byte effective_action[MAX_PLAYERS];
11595   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11596   int i;
11597
11598   for (i = 0; i < MAX_PLAYERS; i++)
11599     effective_action[i] = stored_player[i].effective_action;
11600
11601   GameActions_EM(effective_action, warp_mode);
11602
11603   CheckLevelTime();
11604
11605   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11606 }
11607
11608 void GameActions_RND()
11609 {
11610   int magic_wall_x = 0, magic_wall_y = 0;
11611   int i, x, y, element, graphic;
11612
11613   InitPlayfieldScanModeVars();
11614
11615 #if USE_ONE_MORE_CHANGE_PER_FRAME
11616   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11617   {
11618     SCAN_PLAYFIELD(x, y)
11619     {
11620       ChangeCount[x][y] = 0;
11621       ChangeEvent[x][y] = -1;
11622     }
11623   }
11624 #endif
11625
11626   if (game.set_centered_player)
11627   {
11628     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11629
11630     /* switching to "all players" only possible if all players fit to screen */
11631     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11632     {
11633       game.centered_player_nr_next = game.centered_player_nr;
11634       game.set_centered_player = FALSE;
11635     }
11636
11637     /* do not switch focus to non-existing (or non-active) player */
11638     if (game.centered_player_nr_next >= 0 &&
11639         !stored_player[game.centered_player_nr_next].active)
11640     {
11641       game.centered_player_nr_next = game.centered_player_nr;
11642       game.set_centered_player = FALSE;
11643     }
11644   }
11645
11646   if (game.set_centered_player &&
11647       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11648   {
11649     int sx, sy;
11650
11651     if (game.centered_player_nr_next == -1)
11652     {
11653       setScreenCenteredToAllPlayers(&sx, &sy);
11654     }
11655     else
11656     {
11657       sx = stored_player[game.centered_player_nr_next].jx;
11658       sy = stored_player[game.centered_player_nr_next].jy;
11659     }
11660
11661     game.centered_player_nr = game.centered_player_nr_next;
11662     game.set_centered_player = FALSE;
11663
11664     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11665     DrawGameDoorValues();
11666   }
11667
11668   for (i = 0; i < MAX_PLAYERS; i++)
11669   {
11670     int actual_player_action = stored_player[i].effective_action;
11671
11672 #if 1
11673     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11674        - rnd_equinox_tetrachloride 048
11675        - rnd_equinox_tetrachloride_ii 096
11676        - rnd_emanuel_schmieg 002
11677        - doctor_sloan_ww 001, 020
11678     */
11679     if (stored_player[i].MovPos == 0)
11680       CheckGravityMovement(&stored_player[i]);
11681 #endif
11682
11683     /* overwrite programmed action with tape action */
11684     if (stored_player[i].programmed_action)
11685       actual_player_action = stored_player[i].programmed_action;
11686
11687     PlayerActions(&stored_player[i], actual_player_action);
11688
11689     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11690   }
11691
11692   ScrollScreen(NULL, SCROLL_GO_ON);
11693
11694   /* for backwards compatibility, the following code emulates a fixed bug that
11695      occured when pushing elements (causing elements that just made their last
11696      pushing step to already (if possible) make their first falling step in the
11697      same game frame, which is bad); this code is also needed to use the famous
11698      "spring push bug" which is used in older levels and might be wanted to be
11699      used also in newer levels, but in this case the buggy pushing code is only
11700      affecting the "spring" element and no other elements */
11701
11702   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11703   {
11704     for (i = 0; i < MAX_PLAYERS; i++)
11705     {
11706       struct PlayerInfo *player = &stored_player[i];
11707       int x = player->jx;
11708       int y = player->jy;
11709
11710       if (player->active && player->is_pushing && player->is_moving &&
11711           IS_MOVING(x, y) &&
11712           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11713            Feld[x][y] == EL_SPRING))
11714       {
11715         ContinueMoving(x, y);
11716
11717         /* continue moving after pushing (this is actually a bug) */
11718         if (!IS_MOVING(x, y))
11719           Stop[x][y] = FALSE;
11720       }
11721     }
11722   }
11723
11724 #if 0
11725   debug_print_timestamp(0, "start main loop profiling");
11726 #endif
11727
11728   SCAN_PLAYFIELD(x, y)
11729   {
11730     ChangeCount[x][y] = 0;
11731     ChangeEvent[x][y] = -1;
11732
11733     /* this must be handled before main playfield loop */
11734     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11735     {
11736       MovDelay[x][y]--;
11737       if (MovDelay[x][y] <= 0)
11738         RemoveField(x, y);
11739     }
11740
11741 #if USE_NEW_SNAP_DELAY
11742     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11743     {
11744       MovDelay[x][y]--;
11745       if (MovDelay[x][y] <= 0)
11746       {
11747         RemoveField(x, y);
11748         DrawLevelField(x, y);
11749
11750         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11751       }
11752     }
11753 #endif
11754
11755 #if DEBUG
11756     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11757     {
11758       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11759       printf("GameActions(): This should never happen!\n");
11760
11761       ChangePage[x][y] = -1;
11762     }
11763 #endif
11764
11765     Stop[x][y] = FALSE;
11766     if (WasJustMoving[x][y] > 0)
11767       WasJustMoving[x][y]--;
11768     if (WasJustFalling[x][y] > 0)
11769       WasJustFalling[x][y]--;
11770     if (CheckCollision[x][y] > 0)
11771       CheckCollision[x][y]--;
11772     if (CheckImpact[x][y] > 0)
11773       CheckImpact[x][y]--;
11774
11775     GfxFrame[x][y]++;
11776
11777     /* reset finished pushing action (not done in ContinueMoving() to allow
11778        continuous pushing animation for elements with zero push delay) */
11779     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11780     {
11781       ResetGfxAnimation(x, y);
11782       DrawLevelField(x, y);
11783     }
11784
11785 #if DEBUG
11786     if (IS_BLOCKED(x, y))
11787     {
11788       int oldx, oldy;
11789
11790       Blocked2Moving(x, y, &oldx, &oldy);
11791       if (!IS_MOVING(oldx, oldy))
11792       {
11793         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11794         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11795         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11796         printf("GameActions(): This should never happen!\n");
11797       }
11798     }
11799 #endif
11800   }
11801
11802 #if 0
11803   debug_print_timestamp(0, "- time for pre-main loop:");
11804 #endif
11805
11806 #if 0   // -------------------- !!! TEST ONLY !!! --------------------
11807   SCAN_PLAYFIELD(x, y)
11808   {
11809     element = Feld[x][y];
11810     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11811
11812 #if 1
11813     {
11814 #if 1
11815       int element2 = element;
11816       int graphic2 = graphic;
11817 #else
11818       int element2 = Feld[x][y];
11819       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
11820 #endif
11821       int last_gfx_frame = GfxFrame[x][y];
11822
11823       if (graphic_info[graphic2].anim_global_sync)
11824         GfxFrame[x][y] = FrameCounter;
11825       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
11826         GfxFrame[x][y] = CustomValue[x][y];
11827       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
11828         GfxFrame[x][y] = element_info[element2].collect_score;
11829       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
11830         GfxFrame[x][y] = ChangeDelay[x][y];
11831
11832       if (redraw && GfxFrame[x][y] != last_gfx_frame)
11833         DrawLevelGraphicAnimation(x, y, graphic2);
11834     }
11835 #else
11836     ResetGfxFrame(x, y, TRUE);
11837 #endif
11838
11839 #if 1
11840     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11841         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11842       ResetRandomAnimationValue(x, y);
11843 #endif
11844
11845 #if 1
11846     SetRandomAnimationValue(x, y);
11847 #endif
11848
11849 #if 1
11850     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11851 #endif
11852   }
11853 #endif  // -------------------- !!! TEST ONLY !!! --------------------
11854
11855 #if 0
11856   debug_print_timestamp(0, "- time for TEST loop:     -->");
11857 #endif
11858
11859   SCAN_PLAYFIELD(x, y)
11860   {
11861     element = Feld[x][y];
11862     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11863
11864     ResetGfxFrame(x, y, TRUE);
11865
11866     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11867         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11868       ResetRandomAnimationValue(x, y);
11869
11870     SetRandomAnimationValue(x, y);
11871
11872     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11873
11874     if (IS_INACTIVE(element))
11875     {
11876       if (IS_ANIMATED(graphic))
11877         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11878
11879       continue;
11880     }
11881
11882     /* this may take place after moving, so 'element' may have changed */
11883     if (IS_CHANGING(x, y) &&
11884         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11885     {
11886       int page = element_info[element].event_page_nr[CE_DELAY];
11887
11888 #if 1
11889       HandleElementChange(x, y, page);
11890 #else
11891       if (CAN_CHANGE(element))
11892         HandleElementChange(x, y, page);
11893
11894       if (HAS_ACTION(element))
11895         ExecuteCustomElementAction(x, y, element, page);
11896 #endif
11897
11898       element = Feld[x][y];
11899       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11900     }
11901
11902 #if 0   // ---------------------------------------------------------------------
11903
11904     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11905     {
11906       StartMoving(x, y);
11907
11908       element = Feld[x][y];
11909       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11910
11911       if (IS_ANIMATED(graphic) &&
11912           !IS_MOVING(x, y) &&
11913           !Stop[x][y])
11914         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11915
11916       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11917         DrawTwinkleOnField(x, y);
11918     }
11919     else if (IS_MOVING(x, y))
11920       ContinueMoving(x, y);
11921     else
11922     {
11923       switch (element)
11924       {
11925         case EL_ACID:
11926         case EL_EXIT_OPEN:
11927         case EL_EM_EXIT_OPEN:
11928         case EL_SP_EXIT_OPEN:
11929         case EL_STEEL_EXIT_OPEN:
11930         case EL_EM_STEEL_EXIT_OPEN:
11931         case EL_SP_TERMINAL:
11932         case EL_SP_TERMINAL_ACTIVE:
11933         case EL_EXTRA_TIME:
11934         case EL_SHIELD_NORMAL:
11935         case EL_SHIELD_DEADLY:
11936           if (IS_ANIMATED(graphic))
11937             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11938           break;
11939
11940         case EL_DYNAMITE_ACTIVE:
11941         case EL_EM_DYNAMITE_ACTIVE:
11942         case EL_DYNABOMB_PLAYER_1_ACTIVE:
11943         case EL_DYNABOMB_PLAYER_2_ACTIVE:
11944         case EL_DYNABOMB_PLAYER_3_ACTIVE:
11945         case EL_DYNABOMB_PLAYER_4_ACTIVE:
11946         case EL_SP_DISK_RED_ACTIVE:
11947           CheckDynamite(x, y);
11948           break;
11949
11950         case EL_AMOEBA_GROWING:
11951           AmoebeWaechst(x, y);
11952           break;
11953
11954         case EL_AMOEBA_SHRINKING:
11955           AmoebaDisappearing(x, y);
11956           break;
11957
11958 #if !USE_NEW_AMOEBA_CODE
11959         case EL_AMOEBA_WET:
11960         case EL_AMOEBA_DRY:
11961         case EL_AMOEBA_FULL:
11962         case EL_BD_AMOEBA:
11963         case EL_EMC_DRIPPER:
11964           AmoebeAbleger(x, y);
11965           break;
11966 #endif
11967
11968         case EL_GAME_OF_LIFE:
11969         case EL_BIOMAZE:
11970           Life(x, y);
11971           break;
11972
11973         case EL_EXIT_CLOSED:
11974           CheckExit(x, y);
11975           break;
11976
11977         case EL_EM_EXIT_CLOSED:
11978           CheckExitEM(x, y);
11979           break;
11980
11981         case EL_STEEL_EXIT_CLOSED:
11982           CheckExitSteel(x, y);
11983           break;
11984
11985         case EL_EM_STEEL_EXIT_CLOSED:
11986           CheckExitSteelEM(x, y);
11987           break;
11988
11989         case EL_SP_EXIT_CLOSED:
11990           CheckExitSP(x, y);
11991           break;
11992
11993         case EL_EXPANDABLE_WALL_GROWING:
11994         case EL_EXPANDABLE_STEELWALL_GROWING:
11995           MauerWaechst(x, y);
11996           break;
11997
11998         case EL_EXPANDABLE_WALL:
11999         case EL_EXPANDABLE_WALL_HORIZONTAL:
12000         case EL_EXPANDABLE_WALL_VERTICAL:
12001         case EL_EXPANDABLE_WALL_ANY:
12002         case EL_BD_EXPANDABLE_WALL:
12003           MauerAbleger(x, y);
12004           break;
12005
12006         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12007         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12008         case EL_EXPANDABLE_STEELWALL_ANY:
12009           MauerAblegerStahl(x, y);
12010           break;
12011
12012         case EL_FLAMES:
12013           CheckForDragon(x, y);
12014           break;
12015
12016         case EL_EXPLOSION:
12017           break;
12018
12019         case EL_ELEMENT_SNAPPING:
12020         case EL_DIAGONAL_SHRINKING:
12021         case EL_DIAGONAL_GROWING:
12022         {
12023           graphic =
12024             el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12025
12026           DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12027           break;
12028         }
12029
12030         default:
12031           if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12032             DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12033           break;
12034       }
12035     }
12036
12037 #else   // ---------------------------------------------------------------------
12038
12039     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12040     {
12041       StartMoving(x, y);
12042
12043       element = Feld[x][y];
12044       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12045
12046       if (IS_ANIMATED(graphic) &&
12047           !IS_MOVING(x, y) &&
12048           !Stop[x][y])
12049         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12050
12051       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12052         DrawTwinkleOnField(x, y);
12053     }
12054     else if ((element == EL_ACID ||
12055               element == EL_EXIT_OPEN ||
12056               element == EL_EM_EXIT_OPEN ||
12057               element == EL_SP_EXIT_OPEN ||
12058               element == EL_STEEL_EXIT_OPEN ||
12059               element == EL_EM_STEEL_EXIT_OPEN ||
12060               element == EL_SP_TERMINAL ||
12061               element == EL_SP_TERMINAL_ACTIVE ||
12062               element == EL_EXTRA_TIME ||
12063               element == EL_SHIELD_NORMAL ||
12064               element == EL_SHIELD_DEADLY) &&
12065              IS_ANIMATED(graphic))
12066       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12067     else if (IS_MOVING(x, y))
12068       ContinueMoving(x, y);
12069     else if (IS_ACTIVE_BOMB(element))
12070       CheckDynamite(x, y);
12071     else if (element == EL_AMOEBA_GROWING)
12072       AmoebeWaechst(x, y);
12073     else if (element == EL_AMOEBA_SHRINKING)
12074       AmoebaDisappearing(x, y);
12075
12076 #if !USE_NEW_AMOEBA_CODE
12077     else if (IS_AMOEBALIVE(element))
12078       AmoebeAbleger(x, y);
12079 #endif
12080
12081     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12082       Life(x, y);
12083     else if (element == EL_EXIT_CLOSED)
12084       CheckExit(x, y);
12085     else if (element == EL_EM_EXIT_CLOSED)
12086       CheckExitEM(x, y);
12087     else if (element == EL_STEEL_EXIT_CLOSED)
12088       CheckExitSteel(x, y);
12089     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12090       CheckExitSteelEM(x, y);
12091     else if (element == EL_SP_EXIT_CLOSED)
12092       CheckExitSP(x, y);
12093     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12094              element == EL_EXPANDABLE_STEELWALL_GROWING)
12095       MauerWaechst(x, y);
12096     else if (element == EL_EXPANDABLE_WALL ||
12097              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12098              element == EL_EXPANDABLE_WALL_VERTICAL ||
12099              element == EL_EXPANDABLE_WALL_ANY ||
12100              element == EL_BD_EXPANDABLE_WALL)
12101       MauerAbleger(x, y);
12102     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12103              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12104              element == EL_EXPANDABLE_STEELWALL_ANY)
12105       MauerAblegerStahl(x, y);
12106     else if (element == EL_FLAMES)
12107       CheckForDragon(x, y);
12108     else if (element == EL_EXPLOSION)
12109       ; /* drawing of correct explosion animation is handled separately */
12110     else if (element == EL_ELEMENT_SNAPPING ||
12111              element == EL_DIAGONAL_SHRINKING ||
12112              element == EL_DIAGONAL_GROWING)
12113     {
12114       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12115
12116       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12117     }
12118     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12119       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12120
12121 #endif  // ---------------------------------------------------------------------
12122
12123     if (IS_BELT_ACTIVE(element))
12124       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12125
12126     if (game.magic_wall_active)
12127     {
12128       int jx = local_player->jx, jy = local_player->jy;
12129
12130       /* play the element sound at the position nearest to the player */
12131       if ((element == EL_MAGIC_WALL_FULL ||
12132            element == EL_MAGIC_WALL_ACTIVE ||
12133            element == EL_MAGIC_WALL_EMPTYING ||
12134            element == EL_BD_MAGIC_WALL_FULL ||
12135            element == EL_BD_MAGIC_WALL_ACTIVE ||
12136            element == EL_BD_MAGIC_WALL_EMPTYING ||
12137            element == EL_DC_MAGIC_WALL_FULL ||
12138            element == EL_DC_MAGIC_WALL_ACTIVE ||
12139            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12140           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12141       {
12142         magic_wall_x = x;
12143         magic_wall_y = y;
12144       }
12145     }
12146   }
12147
12148 #if 0
12149   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12150 #endif
12151
12152 #if USE_NEW_AMOEBA_CODE
12153   /* new experimental amoeba growth stuff */
12154   if (!(FrameCounter % 8))
12155   {
12156     static unsigned long random = 1684108901;
12157
12158     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12159     {
12160       x = RND(lev_fieldx);
12161       y = RND(lev_fieldy);
12162       element = Feld[x][y];
12163
12164       if (!IS_PLAYER(x,y) &&
12165           (element == EL_EMPTY ||
12166            CAN_GROW_INTO(element) ||
12167            element == EL_QUICKSAND_EMPTY ||
12168            element == EL_QUICKSAND_FAST_EMPTY ||
12169            element == EL_ACID_SPLASH_LEFT ||
12170            element == EL_ACID_SPLASH_RIGHT))
12171       {
12172         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12173             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12174             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12175             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12176           Feld[x][y] = EL_AMOEBA_DROP;
12177       }
12178
12179       random = random * 129 + 1;
12180     }
12181   }
12182 #endif
12183
12184 #if 0
12185   if (game.explosions_delayed)
12186 #endif
12187   {
12188     game.explosions_delayed = FALSE;
12189
12190     SCAN_PLAYFIELD(x, y)
12191     {
12192       element = Feld[x][y];
12193
12194       if (ExplodeField[x][y])
12195         Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12196       else if (element == EL_EXPLOSION)
12197         Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12198
12199       ExplodeField[x][y] = EX_TYPE_NONE;
12200     }
12201
12202     game.explosions_delayed = TRUE;
12203   }
12204
12205   if (game.magic_wall_active)
12206   {
12207     if (!(game.magic_wall_time_left % 4))
12208     {
12209       int element = Feld[magic_wall_x][magic_wall_y];
12210
12211       if (element == EL_BD_MAGIC_WALL_FULL ||
12212           element == EL_BD_MAGIC_WALL_ACTIVE ||
12213           element == EL_BD_MAGIC_WALL_EMPTYING)
12214         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12215       else if (element == EL_DC_MAGIC_WALL_FULL ||
12216                element == EL_DC_MAGIC_WALL_ACTIVE ||
12217                element == EL_DC_MAGIC_WALL_EMPTYING)
12218         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12219       else
12220         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12221     }
12222
12223     if (game.magic_wall_time_left > 0)
12224     {
12225       game.magic_wall_time_left--;
12226
12227       if (!game.magic_wall_time_left)
12228       {
12229         SCAN_PLAYFIELD(x, y)
12230         {
12231           element = Feld[x][y];
12232
12233           if (element == EL_MAGIC_WALL_ACTIVE ||
12234               element == EL_MAGIC_WALL_FULL)
12235           {
12236             Feld[x][y] = EL_MAGIC_WALL_DEAD;
12237             DrawLevelField(x, y);
12238           }
12239           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12240                    element == EL_BD_MAGIC_WALL_FULL)
12241           {
12242             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12243             DrawLevelField(x, y);
12244           }
12245           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12246                    element == EL_DC_MAGIC_WALL_FULL)
12247           {
12248             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12249             DrawLevelField(x, y);
12250           }
12251         }
12252
12253         game.magic_wall_active = FALSE;
12254       }
12255     }
12256   }
12257
12258   if (game.light_time_left > 0)
12259   {
12260     game.light_time_left--;
12261
12262     if (game.light_time_left == 0)
12263       RedrawAllLightSwitchesAndInvisibleElements();
12264   }
12265
12266   if (game.timegate_time_left > 0)
12267   {
12268     game.timegate_time_left--;
12269
12270     if (game.timegate_time_left == 0)
12271       CloseAllOpenTimegates();
12272   }
12273
12274   if (game.lenses_time_left > 0)
12275   {
12276     game.lenses_time_left--;
12277
12278     if (game.lenses_time_left == 0)
12279       RedrawAllInvisibleElementsForLenses();
12280   }
12281
12282   if (game.magnify_time_left > 0)
12283   {
12284     game.magnify_time_left--;
12285
12286     if (game.magnify_time_left == 0)
12287       RedrawAllInvisibleElementsForMagnifier();
12288   }
12289
12290   for (i = 0; i < MAX_PLAYERS; i++)
12291   {
12292     struct PlayerInfo *player = &stored_player[i];
12293
12294     if (SHIELD_ON(player))
12295     {
12296       if (player->shield_deadly_time_left)
12297         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12298       else if (player->shield_normal_time_left)
12299         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12300     }
12301   }
12302
12303   CheckLevelTime();
12304
12305   DrawAllPlayers();
12306   PlayAllPlayersSound();
12307
12308   if (options.debug)                    /* calculate frames per second */
12309   {
12310     static unsigned long fps_counter = 0;
12311     static int fps_frames = 0;
12312     unsigned long fps_delay_ms = Counter() - fps_counter;
12313
12314     fps_frames++;
12315
12316     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
12317     {
12318       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12319
12320       fps_frames = 0;
12321       fps_counter = Counter();
12322     }
12323
12324     redraw_mask |= REDRAW_FPS;
12325   }
12326
12327   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
12328
12329   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
12330   {
12331     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
12332
12333     local_player->show_envelope = 0;
12334   }
12335
12336 #if 0
12337   debug_print_timestamp(0, "stop main loop profiling ");
12338   printf("----------------------------------------------------------\n");
12339 #endif
12340
12341   /* use random number generator in every frame to make it less predictable */
12342   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12343     RND(1);
12344 }
12345
12346 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12347 {
12348   int min_x = x, min_y = y, max_x = x, max_y = y;
12349   int i;
12350
12351   for (i = 0; i < MAX_PLAYERS; i++)
12352   {
12353     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12354
12355     if (!stored_player[i].active || &stored_player[i] == player)
12356       continue;
12357
12358     min_x = MIN(min_x, jx);
12359     min_y = MIN(min_y, jy);
12360     max_x = MAX(max_x, jx);
12361     max_y = MAX(max_y, jy);
12362   }
12363
12364   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
12365 }
12366
12367 static boolean AllPlayersInVisibleScreen()
12368 {
12369   int i;
12370
12371   for (i = 0; i < MAX_PLAYERS; i++)
12372   {
12373     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12374
12375     if (!stored_player[i].active)
12376       continue;
12377
12378     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12379       return FALSE;
12380   }
12381
12382   return TRUE;
12383 }
12384
12385 void ScrollLevel(int dx, int dy)
12386 {
12387 #if 1
12388   static Bitmap *bitmap_db_field2 = NULL;
12389   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12390   int x, y;
12391 #else
12392   int i, x, y;
12393 #endif
12394
12395 #if 0
12396   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
12397   /* only horizontal XOR vertical scroll direction allowed */
12398   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
12399     return;
12400 #endif
12401
12402 #if 1
12403   if (bitmap_db_field2 == NULL)
12404     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
12405
12406   /* needed when blitting directly to same bitmap -- should not be needed with
12407      recent SDL libraries, but apparently does not work in 1.2.11 directly */
12408   BlitBitmap(drawto_field, bitmap_db_field2,
12409              FX + TILEX * (dx == -1) - softscroll_offset,
12410              FY + TILEY * (dy == -1) - softscroll_offset,
12411              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12412              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12413              FX + TILEX * (dx == 1) - softscroll_offset,
12414              FY + TILEY * (dy == 1) - softscroll_offset);
12415   BlitBitmap(bitmap_db_field2, drawto_field,
12416              FX + TILEX * (dx == 1) - softscroll_offset,
12417              FY + TILEY * (dy == 1) - softscroll_offset,
12418              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12419              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12420              FX + TILEX * (dx == 1) - softscroll_offset,
12421              FY + TILEY * (dy == 1) - softscroll_offset);
12422
12423 #else
12424
12425 #if 1
12426   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
12427   int xsize = (BX2 - BX1 + 1);
12428   int ysize = (BY2 - BY1 + 1);
12429   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
12430   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
12431   int step  = (start < end ? +1 : -1);
12432
12433   for (i = start; i != end; i += step)
12434   {
12435     BlitBitmap(drawto_field, drawto_field,
12436                FX + TILEX * (dx != 0 ? i + step : 0),
12437                FY + TILEY * (dy != 0 ? i + step : 0),
12438                TILEX * (dx != 0 ? 1 : xsize),
12439                TILEY * (dy != 0 ? 1 : ysize),
12440                FX + TILEX * (dx != 0 ? i : 0),
12441                FY + TILEY * (dy != 0 ? i : 0));
12442   }
12443
12444 #else
12445
12446   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
12447
12448   BlitBitmap(drawto_field, drawto_field,
12449              FX + TILEX * (dx == -1) - softscroll_offset,
12450              FY + TILEY * (dy == -1) - softscroll_offset,
12451              SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
12452              SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
12453              FX + TILEX * (dx == 1) - softscroll_offset,
12454              FY + TILEY * (dy == 1) - softscroll_offset);
12455 #endif
12456 #endif
12457
12458   if (dx != 0)
12459   {
12460     x = (dx == 1 ? BX1 : BX2);
12461     for (y = BY1; y <= BY2; y++)
12462       DrawScreenField(x, y);
12463   }
12464
12465   if (dy != 0)
12466   {
12467     y = (dy == 1 ? BY1 : BY2);
12468     for (x = BX1; x <= BX2; x++)
12469       DrawScreenField(x, y);
12470   }
12471
12472   redraw_mask |= REDRAW_FIELD;
12473 }
12474
12475 static boolean canFallDown(struct PlayerInfo *player)
12476 {
12477   int jx = player->jx, jy = player->jy;
12478
12479   return (IN_LEV_FIELD(jx, jy + 1) &&
12480           (IS_FREE(jx, jy + 1) ||
12481            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12482           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
12483           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
12484 }
12485
12486 static boolean canPassField(int x, int y, int move_dir)
12487 {
12488   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12489   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12490   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12491   int nextx = x + dx;
12492   int nexty = y + dy;
12493   int element = Feld[x][y];
12494
12495   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12496           !CAN_MOVE(element) &&
12497           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12498           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
12499           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12500 }
12501
12502 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12503 {
12504   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12505   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12506   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12507   int newx = x + dx;
12508   int newy = y + dy;
12509
12510   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12511           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
12512           (IS_DIGGABLE(Feld[newx][newy]) ||
12513            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
12514            canPassField(newx, newy, move_dir)));
12515 }
12516
12517 static void CheckGravityMovement(struct PlayerInfo *player)
12518 {
12519 #if USE_PLAYER_GRAVITY
12520   if (player->gravity && !player->programmed_action)
12521 #else
12522   if (game.gravity && !player->programmed_action)
12523 #endif
12524   {
12525     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12526     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12527     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12528     int jx = player->jx, jy = player->jy;
12529     boolean player_is_moving_to_valid_field =
12530       (!player_is_snapping &&
12531        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12532         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12533     boolean player_can_fall_down = canFallDown(player);
12534
12535     if (player_can_fall_down &&
12536         !player_is_moving_to_valid_field)
12537       player->programmed_action = MV_DOWN;
12538   }
12539 }
12540
12541 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12542 {
12543   return CheckGravityMovement(player);
12544
12545 #if USE_PLAYER_GRAVITY
12546   if (player->gravity && !player->programmed_action)
12547 #else
12548   if (game.gravity && !player->programmed_action)
12549 #endif
12550   {
12551     int jx = player->jx, jy = player->jy;
12552     boolean field_under_player_is_free =
12553       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12554     boolean player_is_standing_on_valid_field =
12555       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
12556        (IS_WALKABLE(Feld[jx][jy]) &&
12557         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
12558
12559     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12560       player->programmed_action = MV_DOWN;
12561   }
12562 }
12563
12564 /*
12565   MovePlayerOneStep()
12566   -----------------------------------------------------------------------------
12567   dx, dy:               direction (non-diagonal) to try to move the player to
12568   real_dx, real_dy:     direction as read from input device (can be diagonal)
12569 */
12570
12571 boolean MovePlayerOneStep(struct PlayerInfo *player,
12572                           int dx, int dy, int real_dx, int real_dy)
12573 {
12574   int jx = player->jx, jy = player->jy;
12575   int new_jx = jx + dx, new_jy = jy + dy;
12576 #if !USE_FIXED_DONT_RUN_INTO
12577   int element;
12578 #endif
12579   int can_move;
12580   boolean player_can_move = !player->cannot_move;
12581
12582   if (!player->active || (!dx && !dy))
12583     return MP_NO_ACTION;
12584
12585   player->MovDir = (dx < 0 ? MV_LEFT :
12586                     dx > 0 ? MV_RIGHT :
12587                     dy < 0 ? MV_UP :
12588                     dy > 0 ? MV_DOWN :  MV_NONE);
12589
12590   if (!IN_LEV_FIELD(new_jx, new_jy))
12591     return MP_NO_ACTION;
12592
12593   if (!player_can_move)
12594   {
12595     if (player->MovPos == 0)
12596     {
12597       player->is_moving = FALSE;
12598       player->is_digging = FALSE;
12599       player->is_collecting = FALSE;
12600       player->is_snapping = FALSE;
12601       player->is_pushing = FALSE;
12602     }
12603   }
12604
12605 #if 1
12606   if (!options.network && game.centered_player_nr == -1 &&
12607       !AllPlayersInSight(player, new_jx, new_jy))
12608     return MP_NO_ACTION;
12609 #else
12610   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
12611     return MP_NO_ACTION;
12612 #endif
12613
12614 #if !USE_FIXED_DONT_RUN_INTO
12615   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
12616
12617   /* (moved to DigField()) */
12618   if (player_can_move && DONT_RUN_INTO(element))
12619   {
12620     if (element == EL_ACID && dx == 0 && dy == 1)
12621     {
12622       SplashAcid(new_jx, new_jy);
12623       Feld[jx][jy] = EL_PLAYER_1;
12624       InitMovingField(jx, jy, MV_DOWN);
12625       Store[jx][jy] = EL_ACID;
12626       ContinueMoving(jx, jy);
12627       BuryPlayer(player);
12628     }
12629     else
12630       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
12631
12632     return MP_MOVING;
12633   }
12634 #endif
12635
12636   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12637   if (can_move != MP_MOVING)
12638     return can_move;
12639
12640   /* check if DigField() has caused relocation of the player */
12641   if (player->jx != jx || player->jy != jy)
12642     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12643
12644   StorePlayer[jx][jy] = 0;
12645   player->last_jx = jx;
12646   player->last_jy = jy;
12647   player->jx = new_jx;
12648   player->jy = new_jy;
12649   StorePlayer[new_jx][new_jy] = player->element_nr;
12650
12651   if (player->move_delay_value_next != -1)
12652   {
12653     player->move_delay_value = player->move_delay_value_next;
12654     player->move_delay_value_next = -1;
12655   }
12656
12657   player->MovPos =
12658     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12659
12660   player->step_counter++;
12661
12662   PlayerVisit[jx][jy] = FrameCounter;
12663
12664 #if USE_UFAST_PLAYER_EXIT_BUGFIX
12665   player->is_moving = TRUE;
12666 #endif
12667
12668 #if 1
12669   /* should better be called in MovePlayer(), but this breaks some tapes */
12670   ScrollPlayer(player, SCROLL_INIT);
12671 #endif
12672
12673   return MP_MOVING;
12674 }
12675
12676 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12677 {
12678   int jx = player->jx, jy = player->jy;
12679   int old_jx = jx, old_jy = jy;
12680   int moved = MP_NO_ACTION;
12681
12682   if (!player->active)
12683     return FALSE;
12684
12685   if (!dx && !dy)
12686   {
12687     if (player->MovPos == 0)
12688     {
12689       player->is_moving = FALSE;
12690       player->is_digging = FALSE;
12691       player->is_collecting = FALSE;
12692       player->is_snapping = FALSE;
12693       player->is_pushing = FALSE;
12694     }
12695
12696     return FALSE;
12697   }
12698
12699   if (player->move_delay > 0)
12700     return FALSE;
12701
12702   player->move_delay = -1;              /* set to "uninitialized" value */
12703
12704   /* store if player is automatically moved to next field */
12705   player->is_auto_moving = (player->programmed_action != MV_NONE);
12706
12707   /* remove the last programmed player action */
12708   player->programmed_action = 0;
12709
12710   if (player->MovPos)
12711   {
12712     /* should only happen if pre-1.2 tape recordings are played */
12713     /* this is only for backward compatibility */
12714
12715     int original_move_delay_value = player->move_delay_value;
12716
12717 #if DEBUG
12718     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
12719            tape.counter);
12720 #endif
12721
12722     /* scroll remaining steps with finest movement resolution */
12723     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12724
12725     while (player->MovPos)
12726     {
12727       ScrollPlayer(player, SCROLL_GO_ON);
12728       ScrollScreen(NULL, SCROLL_GO_ON);
12729
12730       AdvanceFrameAndPlayerCounters(player->index_nr);
12731
12732       DrawAllPlayers();
12733       BackToFront();
12734     }
12735
12736     player->move_delay_value = original_move_delay_value;
12737   }
12738
12739   player->is_active = FALSE;
12740
12741   if (player->last_move_dir & MV_HORIZONTAL)
12742   {
12743     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12744       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12745   }
12746   else
12747   {
12748     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12749       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12750   }
12751
12752 #if USE_FIXED_BORDER_RUNNING_GFX
12753   if (!moved && !player->is_active)
12754   {
12755     player->is_moving = FALSE;
12756     player->is_digging = FALSE;
12757     player->is_collecting = FALSE;
12758     player->is_snapping = FALSE;
12759     player->is_pushing = FALSE;
12760   }
12761 #endif
12762
12763   jx = player->jx;
12764   jy = player->jy;
12765
12766 #if 1
12767   if (moved & MP_MOVING && !ScreenMovPos &&
12768       (player->index_nr == game.centered_player_nr ||
12769        game.centered_player_nr == -1))
12770 #else
12771   if (moved & MP_MOVING && !ScreenMovPos &&
12772       (player == local_player || !options.network))
12773 #endif
12774   {
12775     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12776     int offset = game.scroll_delay_value;
12777
12778     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12779     {
12780       /* actual player has left the screen -- scroll in that direction */
12781       if (jx != old_jx)         /* player has moved horizontally */
12782         scroll_x += (jx - old_jx);
12783       else                      /* player has moved vertically */
12784         scroll_y += (jy - old_jy);
12785     }
12786     else
12787     {
12788       if (jx != old_jx)         /* player has moved horizontally */
12789       {
12790         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12791             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12792           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12793
12794         /* don't scroll over playfield boundaries */
12795         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12796           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12797
12798         /* don't scroll more than one field at a time */
12799         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12800
12801         /* don't scroll against the player's moving direction */
12802         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12803             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12804           scroll_x = old_scroll_x;
12805       }
12806       else                      /* player has moved vertically */
12807       {
12808         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12809             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12810           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12811
12812         /* don't scroll over playfield boundaries */
12813         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12814           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12815
12816         /* don't scroll more than one field at a time */
12817         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12818
12819         /* don't scroll against the player's moving direction */
12820         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12821             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12822           scroll_y = old_scroll_y;
12823       }
12824     }
12825
12826     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12827     {
12828 #if 1
12829       if (!options.network && game.centered_player_nr == -1 &&
12830           !AllPlayersInVisibleScreen())
12831       {
12832         scroll_x = old_scroll_x;
12833         scroll_y = old_scroll_y;
12834       }
12835       else
12836 #else
12837       if (!options.network && !AllPlayersInVisibleScreen())
12838       {
12839         scroll_x = old_scroll_x;
12840         scroll_y = old_scroll_y;
12841       }
12842       else
12843 #endif
12844       {
12845         ScrollScreen(player, SCROLL_INIT);
12846         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12847       }
12848     }
12849   }
12850
12851   player->StepFrame = 0;
12852
12853   if (moved & MP_MOVING)
12854   {
12855     if (old_jx != jx && old_jy == jy)
12856       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12857     else if (old_jx == jx && old_jy != jy)
12858       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12859
12860     DrawLevelField(jx, jy);     /* for "crumbled sand" */
12861
12862     player->last_move_dir = player->MovDir;
12863     player->is_moving = TRUE;
12864     player->is_snapping = FALSE;
12865     player->is_switching = FALSE;
12866     player->is_dropping = FALSE;
12867     player->is_dropping_pressed = FALSE;
12868     player->drop_pressed_delay = 0;
12869
12870 #if 0
12871     /* should better be called here than above, but this breaks some tapes */
12872     ScrollPlayer(player, SCROLL_INIT);
12873 #endif
12874   }
12875   else
12876   {
12877     CheckGravityMovementWhenNotMoving(player);
12878
12879     player->is_moving = FALSE;
12880
12881     /* at this point, the player is allowed to move, but cannot move right now
12882        (e.g. because of something blocking the way) -- ensure that the player
12883        is also allowed to move in the next frame (in old versions before 3.1.1,
12884        the player was forced to wait again for eight frames before next try) */
12885
12886     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12887       player->move_delay = 0;   /* allow direct movement in the next frame */
12888   }
12889
12890   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12891     player->move_delay = player->move_delay_value;
12892
12893   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12894   {
12895     TestIfPlayerTouchesBadThing(jx, jy);
12896     TestIfPlayerTouchesCustomElement(jx, jy);
12897   }
12898
12899   if (!player->active)
12900     RemovePlayer(player);
12901
12902   return moved;
12903 }
12904
12905 void ScrollPlayer(struct PlayerInfo *player, int mode)
12906 {
12907   int jx = player->jx, jy = player->jy;
12908   int last_jx = player->last_jx, last_jy = player->last_jy;
12909   int move_stepsize = TILEX / player->move_delay_value;
12910
12911 #if USE_NEW_PLAYER_SPEED
12912   if (!player->active)
12913     return;
12914
12915   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12916     return;
12917 #else
12918   if (!player->active || player->MovPos == 0)
12919     return;
12920 #endif
12921
12922   if (mode == SCROLL_INIT)
12923   {
12924     player->actual_frame_counter = FrameCounter;
12925     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12926
12927     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12928         Feld[last_jx][last_jy] == EL_EMPTY)
12929     {
12930       int last_field_block_delay = 0;   /* start with no blocking at all */
12931       int block_delay_adjustment = player->block_delay_adjustment;
12932
12933       /* if player blocks last field, add delay for exactly one move */
12934       if (player->block_last_field)
12935       {
12936         last_field_block_delay += player->move_delay_value;
12937
12938         /* when blocking enabled, prevent moving up despite gravity */
12939 #if USE_PLAYER_GRAVITY
12940         if (player->gravity && player->MovDir == MV_UP)
12941           block_delay_adjustment = -1;
12942 #else
12943         if (game.gravity && player->MovDir == MV_UP)
12944           block_delay_adjustment = -1;
12945 #endif
12946       }
12947
12948       /* add block delay adjustment (also possible when not blocking) */
12949       last_field_block_delay += block_delay_adjustment;
12950
12951       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12952       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12953     }
12954
12955 #if USE_NEW_PLAYER_SPEED
12956     if (player->MovPos != 0)    /* player has not yet reached destination */
12957       return;
12958 #else
12959     return;
12960 #endif
12961   }
12962   else if (!FrameReached(&player->actual_frame_counter, 1))
12963     return;
12964
12965 #if USE_NEW_PLAYER_SPEED
12966   if (player->MovPos != 0)
12967   {
12968     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12969     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12970
12971     /* before DrawPlayer() to draw correct player graphic for this case */
12972     if (player->MovPos == 0)
12973       CheckGravityMovement(player);
12974   }
12975 #else
12976   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12977   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12978
12979   /* before DrawPlayer() to draw correct player graphic for this case */
12980   if (player->MovPos == 0)
12981     CheckGravityMovement(player);
12982 #endif
12983
12984   if (player->MovPos == 0)      /* player reached destination field */
12985   {
12986     if (player->move_delay_reset_counter > 0)
12987     {
12988       player->move_delay_reset_counter--;
12989
12990       if (player->move_delay_reset_counter == 0)
12991       {
12992         /* continue with normal speed after quickly moving through gate */
12993         HALVE_PLAYER_SPEED(player);
12994
12995         /* be able to make the next move without delay */
12996         player->move_delay = 0;
12997       }
12998     }
12999
13000     player->last_jx = jx;
13001     player->last_jy = jy;
13002
13003     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13004         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13005         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13006         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13007         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13008         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
13009     {
13010       DrawPlayer(player);       /* needed here only to cleanup last field */
13011       RemovePlayer(player);
13012
13013       if (local_player->friends_still_needed == 0 ||
13014           IS_SP_ELEMENT(Feld[jx][jy]))
13015         PlayerWins(player);
13016     }
13017
13018     /* this breaks one level: "machine", level 000 */
13019     {
13020       int move_direction = player->MovDir;
13021       int enter_side = MV_DIR_OPPOSITE(move_direction);
13022       int leave_side = move_direction;
13023       int old_jx = last_jx;
13024       int old_jy = last_jy;
13025       int old_element = Feld[old_jx][old_jy];
13026       int new_element = Feld[jx][jy];
13027
13028       if (IS_CUSTOM_ELEMENT(old_element))
13029         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13030                                    CE_LEFT_BY_PLAYER,
13031                                    player->index_bit, leave_side);
13032
13033       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13034                                           CE_PLAYER_LEAVES_X,
13035                                           player->index_bit, leave_side);
13036
13037       if (IS_CUSTOM_ELEMENT(new_element))
13038         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13039                                    player->index_bit, enter_side);
13040
13041       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13042                                           CE_PLAYER_ENTERS_X,
13043                                           player->index_bit, enter_side);
13044
13045       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13046                                         CE_MOVE_OF_X, move_direction);
13047     }
13048
13049     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13050     {
13051       TestIfPlayerTouchesBadThing(jx, jy);
13052       TestIfPlayerTouchesCustomElement(jx, jy);
13053
13054       /* needed because pushed element has not yet reached its destination,
13055          so it would trigger a change event at its previous field location */
13056       if (!player->is_pushing)
13057         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
13058
13059       if (!player->active)
13060         RemovePlayer(player);
13061     }
13062
13063     if (!local_player->LevelSolved && level.use_step_counter)
13064     {
13065       int i;
13066
13067       TimePlayed++;
13068
13069       if (TimeLeft > 0)
13070       {
13071         TimeLeft--;
13072
13073         if (TimeLeft <= 10 && setup.time_limit)
13074           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13075
13076 #if 1
13077         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13078
13079         DisplayGameControlValues();
13080 #else
13081         DrawGameValue_Time(TimeLeft);
13082 #endif
13083
13084         if (!TimeLeft && setup.time_limit)
13085           for (i = 0; i < MAX_PLAYERS; i++)
13086             KillPlayer(&stored_player[i]);
13087       }
13088 #if 1
13089       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13090       {
13091         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13092
13093         DisplayGameControlValues();
13094       }
13095 #else
13096       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13097         DrawGameValue_Time(TimePlayed);
13098 #endif
13099     }
13100
13101     if (tape.single_step && tape.recording && !tape.pausing &&
13102         !player->programmed_action)
13103       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13104   }
13105 }
13106
13107 void ScrollScreen(struct PlayerInfo *player, int mode)
13108 {
13109   static unsigned long screen_frame_counter = 0;
13110
13111   if (mode == SCROLL_INIT)
13112   {
13113     /* set scrolling step size according to actual player's moving speed */
13114     ScrollStepSize = TILEX / player->move_delay_value;
13115
13116     screen_frame_counter = FrameCounter;
13117     ScreenMovDir = player->MovDir;
13118     ScreenMovPos = player->MovPos;
13119     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13120     return;
13121   }
13122   else if (!FrameReached(&screen_frame_counter, 1))
13123     return;
13124
13125   if (ScreenMovPos)
13126   {
13127     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13128     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13129     redraw_mask |= REDRAW_FIELD;
13130   }
13131   else
13132     ScreenMovDir = MV_NONE;
13133 }
13134
13135 void TestIfPlayerTouchesCustomElement(int x, int y)
13136 {
13137   static int xy[4][2] =
13138   {
13139     { 0, -1 },
13140     { -1, 0 },
13141     { +1, 0 },
13142     { 0, +1 }
13143   };
13144   static int trigger_sides[4][2] =
13145   {
13146     /* center side       border side */
13147     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13148     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13149     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13150     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13151   };
13152   static int touch_dir[4] =
13153   {
13154     MV_LEFT | MV_RIGHT,
13155     MV_UP   | MV_DOWN,
13156     MV_UP   | MV_DOWN,
13157     MV_LEFT | MV_RIGHT
13158   };
13159   int center_element = Feld[x][y];      /* should always be non-moving! */
13160   int i;
13161
13162   for (i = 0; i < NUM_DIRECTIONS; i++)
13163   {
13164     int xx = x + xy[i][0];
13165     int yy = y + xy[i][1];
13166     int center_side = trigger_sides[i][0];
13167     int border_side = trigger_sides[i][1];
13168     int border_element;
13169
13170     if (!IN_LEV_FIELD(xx, yy))
13171       continue;
13172
13173     if (IS_PLAYER(x, y))
13174     {
13175       struct PlayerInfo *player = PLAYERINFO(x, y);
13176
13177       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13178         border_element = Feld[xx][yy];          /* may be moving! */
13179       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13180         border_element = Feld[xx][yy];
13181       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
13182         border_element = MovingOrBlocked2Element(xx, yy);
13183       else
13184         continue;               /* center and border element do not touch */
13185
13186       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13187                                  player->index_bit, border_side);
13188       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13189                                           CE_PLAYER_TOUCHES_X,
13190                                           player->index_bit, border_side);
13191     }
13192     else if (IS_PLAYER(xx, yy))
13193     {
13194       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13195
13196       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13197       {
13198         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13199           continue;             /* center and border element do not touch */
13200       }
13201
13202       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13203                                  player->index_bit, center_side);
13204       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13205                                           CE_PLAYER_TOUCHES_X,
13206                                           player->index_bit, center_side);
13207       break;
13208     }
13209   }
13210 }
13211
13212 #if USE_ELEMENT_TOUCHING_BUGFIX
13213
13214 void TestIfElementTouchesCustomElement(int x, int y)
13215 {
13216   static int xy[4][2] =
13217   {
13218     { 0, -1 },
13219     { -1, 0 },
13220     { +1, 0 },
13221     { 0, +1 }
13222   };
13223   static int trigger_sides[4][2] =
13224   {
13225     /* center side      border side */
13226     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13227     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13228     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13229     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13230   };
13231   static int touch_dir[4] =
13232   {
13233     MV_LEFT | MV_RIGHT,
13234     MV_UP   | MV_DOWN,
13235     MV_UP   | MV_DOWN,
13236     MV_LEFT | MV_RIGHT
13237   };
13238   boolean change_center_element = FALSE;
13239   int center_element = Feld[x][y];      /* should always be non-moving! */
13240   int border_element_old[NUM_DIRECTIONS];
13241   int i;
13242
13243   for (i = 0; i < NUM_DIRECTIONS; i++)
13244   {
13245     int xx = x + xy[i][0];
13246     int yy = y + xy[i][1];
13247     int border_element;
13248
13249     border_element_old[i] = -1;
13250
13251     if (!IN_LEV_FIELD(xx, yy))
13252       continue;
13253
13254     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13255       border_element = Feld[xx][yy];    /* may be moving! */
13256     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13257       border_element = Feld[xx][yy];
13258     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13259       border_element = MovingOrBlocked2Element(xx, yy);
13260     else
13261       continue;                 /* center and border element do not touch */
13262
13263     border_element_old[i] = border_element;
13264   }
13265
13266   for (i = 0; i < NUM_DIRECTIONS; i++)
13267   {
13268     int xx = x + xy[i][0];
13269     int yy = y + xy[i][1];
13270     int center_side = trigger_sides[i][0];
13271     int border_element = border_element_old[i];
13272
13273     if (border_element == -1)
13274       continue;
13275
13276     /* check for change of border element */
13277     CheckElementChangeBySide(xx, yy, border_element, center_element,
13278                              CE_TOUCHING_X, center_side);
13279   }
13280
13281   for (i = 0; i < NUM_DIRECTIONS; i++)
13282   {
13283     int border_side = trigger_sides[i][1];
13284     int border_element = border_element_old[i];
13285
13286     if (border_element == -1)
13287       continue;
13288
13289     /* check for change of center element (but change it only once) */
13290     if (!change_center_element)
13291       change_center_element =
13292         CheckElementChangeBySide(x, y, center_element, border_element,
13293                                  CE_TOUCHING_X, border_side);
13294   }
13295 }
13296
13297 #else
13298
13299 void TestIfElementTouchesCustomElement_OLD(int x, int y)
13300 {
13301   static int xy[4][2] =
13302   {
13303     { 0, -1 },
13304     { -1, 0 },
13305     { +1, 0 },
13306     { 0, +1 }
13307   };
13308   static int trigger_sides[4][2] =
13309   {
13310     /* center side      border side */
13311     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
13312     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
13313     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
13314     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
13315   };
13316   static int touch_dir[4] =
13317   {
13318     MV_LEFT | MV_RIGHT,
13319     MV_UP   | MV_DOWN,
13320     MV_UP   | MV_DOWN,
13321     MV_LEFT | MV_RIGHT
13322   };
13323   boolean change_center_element = FALSE;
13324   int center_element = Feld[x][y];      /* should always be non-moving! */
13325   int i;
13326
13327   for (i = 0; i < NUM_DIRECTIONS; i++)
13328   {
13329     int xx = x + xy[i][0];
13330     int yy = y + xy[i][1];
13331     int center_side = trigger_sides[i][0];
13332     int border_side = trigger_sides[i][1];
13333     int border_element;
13334
13335     if (!IN_LEV_FIELD(xx, yy))
13336       continue;
13337
13338     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13339       border_element = Feld[xx][yy];    /* may be moving! */
13340     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13341       border_element = Feld[xx][yy];
13342     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
13343       border_element = MovingOrBlocked2Element(xx, yy);
13344     else
13345       continue;                 /* center and border element do not touch */
13346
13347     /* check for change of center element (but change it only once) */
13348     if (!change_center_element)
13349       change_center_element =
13350         CheckElementChangeBySide(x, y, center_element, border_element,
13351                                  CE_TOUCHING_X, border_side);
13352
13353     /* check for change of border element */
13354     CheckElementChangeBySide(xx, yy, border_element, center_element,
13355                              CE_TOUCHING_X, center_side);
13356   }
13357 }
13358
13359 #endif
13360
13361 void TestIfElementHitsCustomElement(int x, int y, int direction)
13362 {
13363   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13364   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13365   int hitx = x + dx, hity = y + dy;
13366   int hitting_element = Feld[x][y];
13367   int touched_element;
13368
13369   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13370     return;
13371
13372   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13373                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13374
13375   if (IN_LEV_FIELD(hitx, hity))
13376   {
13377     int opposite_direction = MV_DIR_OPPOSITE(direction);
13378     int hitting_side = direction;
13379     int touched_side = opposite_direction;
13380     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13381                           MovDir[hitx][hity] != direction ||
13382                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13383
13384     object_hit = TRUE;
13385
13386     if (object_hit)
13387     {
13388       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13389                                CE_HITTING_X, touched_side);
13390
13391       CheckElementChangeBySide(hitx, hity, touched_element,
13392                                hitting_element, CE_HIT_BY_X, hitting_side);
13393
13394       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13395                                CE_HIT_BY_SOMETHING, opposite_direction);
13396     }
13397   }
13398
13399   /* "hitting something" is also true when hitting the playfield border */
13400   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13401                            CE_HITTING_SOMETHING, direction);
13402 }
13403
13404 #if 0
13405 void TestIfElementSmashesCustomElement(int x, int y, int direction)
13406 {
13407   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13408   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13409   int hitx = x + dx, hity = y + dy;
13410   int hitting_element = Feld[x][y];
13411   int touched_element;
13412 #if 0
13413   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
13414                         !IS_FREE(hitx, hity) &&
13415                         (!IS_MOVING(hitx, hity) ||
13416                          MovDir[hitx][hity] != direction ||
13417                          ABS(MovPos[hitx][hity]) <= TILEY / 2));
13418 #endif
13419
13420   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13421     return;
13422
13423 #if 0
13424   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
13425     return;
13426 #endif
13427
13428   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13429                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13430
13431   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13432                            EP_CAN_SMASH_EVERYTHING, direction);
13433
13434   if (IN_LEV_FIELD(hitx, hity))
13435   {
13436     int opposite_direction = MV_DIR_OPPOSITE(direction);
13437     int hitting_side = direction;
13438     int touched_side = opposite_direction;
13439 #if 0
13440     int touched_element = MovingOrBlocked2Element(hitx, hity);
13441 #endif
13442 #if 1
13443     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13444                           MovDir[hitx][hity] != direction ||
13445                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13446
13447     object_hit = TRUE;
13448 #endif
13449
13450     if (object_hit)
13451     {
13452       int i;
13453
13454       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13455                                CE_SMASHED_BY_SOMETHING, opposite_direction);
13456
13457       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13458                                CE_OTHER_IS_SMASHING, touched_side);
13459
13460       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13461                                CE_OTHER_GETS_SMASHED, hitting_side);
13462     }
13463   }
13464 }
13465 #endif
13466
13467 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13468 {
13469   int i, kill_x = -1, kill_y = -1;
13470
13471   int bad_element = -1;
13472   static int test_xy[4][2] =
13473   {
13474     { 0, -1 },
13475     { -1, 0 },
13476     { +1, 0 },
13477     { 0, +1 }
13478   };
13479   static int test_dir[4] =
13480   {
13481     MV_UP,
13482     MV_LEFT,
13483     MV_RIGHT,
13484     MV_DOWN
13485   };
13486
13487   for (i = 0; i < NUM_DIRECTIONS; i++)
13488   {
13489     int test_x, test_y, test_move_dir, test_element;
13490
13491     test_x = good_x + test_xy[i][0];
13492     test_y = good_y + test_xy[i][1];
13493
13494     if (!IN_LEV_FIELD(test_x, test_y))
13495       continue;
13496
13497     test_move_dir =
13498       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13499
13500     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13501
13502     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13503        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13504     */
13505     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13506         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13507     {
13508       kill_x = test_x;
13509       kill_y = test_y;
13510       bad_element = test_element;
13511
13512       break;
13513     }
13514   }
13515
13516   if (kill_x != -1 || kill_y != -1)
13517   {
13518     if (IS_PLAYER(good_x, good_y))
13519     {
13520       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13521
13522       if (player->shield_deadly_time_left > 0 &&
13523           !IS_INDESTRUCTIBLE(bad_element))
13524         Bang(kill_x, kill_y);
13525       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13526         KillPlayer(player);
13527     }
13528     else
13529       Bang(good_x, good_y);
13530   }
13531 }
13532
13533 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13534 {
13535   int i, kill_x = -1, kill_y = -1;
13536   int bad_element = Feld[bad_x][bad_y];
13537   static int test_xy[4][2] =
13538   {
13539     { 0, -1 },
13540     { -1, 0 },
13541     { +1, 0 },
13542     { 0, +1 }
13543   };
13544   static int touch_dir[4] =
13545   {
13546     MV_LEFT | MV_RIGHT,
13547     MV_UP   | MV_DOWN,
13548     MV_UP   | MV_DOWN,
13549     MV_LEFT | MV_RIGHT
13550   };
13551   static int test_dir[4] =
13552   {
13553     MV_UP,
13554     MV_LEFT,
13555     MV_RIGHT,
13556     MV_DOWN
13557   };
13558
13559   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
13560     return;
13561
13562   for (i = 0; i < NUM_DIRECTIONS; i++)
13563   {
13564     int test_x, test_y, test_move_dir, test_element;
13565
13566     test_x = bad_x + test_xy[i][0];
13567     test_y = bad_y + test_xy[i][1];
13568     if (!IN_LEV_FIELD(test_x, test_y))
13569       continue;
13570
13571     test_move_dir =
13572       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13573
13574     test_element = Feld[test_x][test_y];
13575
13576     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13577        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13578     */
13579     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13580         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13581     {
13582       /* good thing is player or penguin that does not move away */
13583       if (IS_PLAYER(test_x, test_y))
13584       {
13585         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13586
13587         if (bad_element == EL_ROBOT && player->is_moving)
13588           continue;     /* robot does not kill player if he is moving */
13589
13590         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13591         {
13592           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13593             continue;           /* center and border element do not touch */
13594         }
13595
13596         kill_x = test_x;
13597         kill_y = test_y;
13598         break;
13599       }
13600       else if (test_element == EL_PENGUIN)
13601       {
13602         kill_x = test_x;
13603         kill_y = test_y;
13604         break;
13605       }
13606     }
13607   }
13608
13609   if (kill_x != -1 || kill_y != -1)
13610   {
13611     if (IS_PLAYER(kill_x, kill_y))
13612     {
13613       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13614
13615       if (player->shield_deadly_time_left > 0 &&
13616           !IS_INDESTRUCTIBLE(bad_element))
13617         Bang(bad_x, bad_y);
13618       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13619         KillPlayer(player);
13620     }
13621     else
13622       Bang(kill_x, kill_y);
13623   }
13624 }
13625
13626 void TestIfPlayerTouchesBadThing(int x, int y)
13627 {
13628   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13629 }
13630
13631 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13632 {
13633   TestIfGoodThingHitsBadThing(x, y, move_dir);
13634 }
13635
13636 void TestIfBadThingTouchesPlayer(int x, int y)
13637 {
13638   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13639 }
13640
13641 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13642 {
13643   TestIfBadThingHitsGoodThing(x, y, move_dir);
13644 }
13645
13646 void TestIfFriendTouchesBadThing(int x, int y)
13647 {
13648   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13649 }
13650
13651 void TestIfBadThingTouchesFriend(int x, int y)
13652 {
13653   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13654 }
13655
13656 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13657 {
13658   int i, kill_x = bad_x, kill_y = bad_y;
13659   static int xy[4][2] =
13660   {
13661     { 0, -1 },
13662     { -1, 0 },
13663     { +1, 0 },
13664     { 0, +1 }
13665   };
13666
13667   for (i = 0; i < NUM_DIRECTIONS; i++)
13668   {
13669     int x, y, element;
13670
13671     x = bad_x + xy[i][0];
13672     y = bad_y + xy[i][1];
13673     if (!IN_LEV_FIELD(x, y))
13674       continue;
13675
13676     element = Feld[x][y];
13677     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13678         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13679     {
13680       kill_x = x;
13681       kill_y = y;
13682       break;
13683     }
13684   }
13685
13686   if (kill_x != bad_x || kill_y != bad_y)
13687     Bang(bad_x, bad_y);
13688 }
13689
13690 void KillPlayer(struct PlayerInfo *player)
13691 {
13692   int jx = player->jx, jy = player->jy;
13693
13694   if (!player->active)
13695     return;
13696
13697   /* the following code was introduced to prevent an infinite loop when calling
13698      -> Bang()
13699      -> CheckTriggeredElementChangeExt()
13700      -> ExecuteCustomElementAction()
13701      -> KillPlayer()
13702      -> (infinitely repeating the above sequence of function calls)
13703      which occurs when killing the player while having a CE with the setting
13704      "kill player X when explosion of <player X>"; the solution using a new
13705      field "player->killed" was chosen for backwards compatibility, although
13706      clever use of the fields "player->active" etc. would probably also work */
13707 #if 1
13708   if (player->killed)
13709     return;
13710 #endif
13711
13712   player->killed = TRUE;
13713
13714   /* remove accessible field at the player's position */
13715   Feld[jx][jy] = EL_EMPTY;
13716
13717   /* deactivate shield (else Bang()/Explode() would not work right) */
13718   player->shield_normal_time_left = 0;
13719   player->shield_deadly_time_left = 0;
13720
13721   Bang(jx, jy);
13722   BuryPlayer(player);
13723 }
13724
13725 static void KillPlayerUnlessEnemyProtected(int x, int y)
13726 {
13727   if (!PLAYER_ENEMY_PROTECTED(x, y))
13728     KillPlayer(PLAYERINFO(x, y));
13729 }
13730
13731 static void KillPlayerUnlessExplosionProtected(int x, int y)
13732 {
13733   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13734     KillPlayer(PLAYERINFO(x, y));
13735 }
13736
13737 void BuryPlayer(struct PlayerInfo *player)
13738 {
13739   int jx = player->jx, jy = player->jy;
13740
13741   if (!player->active)
13742     return;
13743
13744   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13745   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13746
13747   player->GameOver = TRUE;
13748   RemovePlayer(player);
13749 }
13750
13751 void RemovePlayer(struct PlayerInfo *player)
13752 {
13753   int jx = player->jx, jy = player->jy;
13754   int i, found = FALSE;
13755
13756   player->present = FALSE;
13757   player->active = FALSE;
13758
13759   if (!ExplodeField[jx][jy])
13760     StorePlayer[jx][jy] = 0;
13761
13762   if (player->is_moving)
13763     DrawLevelField(player->last_jx, player->last_jy);
13764
13765   for (i = 0; i < MAX_PLAYERS; i++)
13766     if (stored_player[i].active)
13767       found = TRUE;
13768
13769   if (!found)
13770     AllPlayersGone = TRUE;
13771
13772   ExitX = ZX = jx;
13773   ExitY = ZY = jy;
13774 }
13775
13776 #if USE_NEW_SNAP_DELAY
13777 static void setFieldForSnapping(int x, int y, int element, int direction)
13778 {
13779   struct ElementInfo *ei = &element_info[element];
13780   int direction_bit = MV_DIR_TO_BIT(direction);
13781   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13782   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13783                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13784
13785   Feld[x][y] = EL_ELEMENT_SNAPPING;
13786   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13787
13788   ResetGfxAnimation(x, y);
13789
13790   GfxElement[x][y] = element;
13791   GfxAction[x][y] = action;
13792   GfxDir[x][y] = direction;
13793   GfxFrame[x][y] = -1;
13794 }
13795 #endif
13796
13797 /*
13798   =============================================================================
13799   checkDiagonalPushing()
13800   -----------------------------------------------------------------------------
13801   check if diagonal input device direction results in pushing of object
13802   (by checking if the alternative direction is walkable, diggable, ...)
13803   =============================================================================
13804 */
13805
13806 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13807                                     int x, int y, int real_dx, int real_dy)
13808 {
13809   int jx, jy, dx, dy, xx, yy;
13810
13811   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13812     return TRUE;
13813
13814   /* diagonal direction: check alternative direction */
13815   jx = player->jx;
13816   jy = player->jy;
13817   dx = x - jx;
13818   dy = y - jy;
13819   xx = jx + (dx == 0 ? real_dx : 0);
13820   yy = jy + (dy == 0 ? real_dy : 0);
13821
13822   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13823 }
13824
13825 /*
13826   =============================================================================
13827   DigField()
13828   -----------------------------------------------------------------------------
13829   x, y:                 field next to player (non-diagonal) to try to dig to
13830   real_dx, real_dy:     direction as read from input device (can be diagonal)
13831   =============================================================================
13832 */
13833
13834 int DigField(struct PlayerInfo *player,
13835              int oldx, int oldy, int x, int y,
13836              int real_dx, int real_dy, int mode)
13837 {
13838   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13839   boolean player_was_pushing = player->is_pushing;
13840   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13841   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13842   int jx = oldx, jy = oldy;
13843   int dx = x - jx, dy = y - jy;
13844   int nextx = x + dx, nexty = y + dy;
13845   int move_direction = (dx == -1 ? MV_LEFT  :
13846                         dx == +1 ? MV_RIGHT :
13847                         dy == -1 ? MV_UP    :
13848                         dy == +1 ? MV_DOWN  : MV_NONE);
13849   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13850   int dig_side = MV_DIR_OPPOSITE(move_direction);
13851   int old_element = Feld[jx][jy];
13852 #if USE_FIXED_DONT_RUN_INTO
13853   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13854 #else
13855   int element;
13856 #endif
13857   int collect_count;
13858
13859   if (is_player)                /* function can also be called by EL_PENGUIN */
13860   {
13861     if (player->MovPos == 0)
13862     {
13863       player->is_digging = FALSE;
13864       player->is_collecting = FALSE;
13865     }
13866
13867     if (player->MovPos == 0)    /* last pushing move finished */
13868       player->is_pushing = FALSE;
13869
13870     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13871     {
13872       player->is_switching = FALSE;
13873       player->push_delay = -1;
13874
13875       return MP_NO_ACTION;
13876     }
13877   }
13878
13879 #if !USE_FIXED_DONT_RUN_INTO
13880   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13881     return MP_NO_ACTION;
13882 #endif
13883
13884   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13885     old_element = Back[jx][jy];
13886
13887   /* in case of element dropped at player position, check background */
13888   else if (Back[jx][jy] != EL_EMPTY &&
13889            game.engine_version >= VERSION_IDENT(2,2,0,0))
13890     old_element = Back[jx][jy];
13891
13892   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13893     return MP_NO_ACTION;        /* field has no opening in this direction */
13894
13895   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13896     return MP_NO_ACTION;        /* field has no opening in this direction */
13897
13898 #if USE_FIXED_DONT_RUN_INTO
13899   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13900   {
13901     SplashAcid(x, y);
13902
13903     Feld[jx][jy] = player->artwork_element;
13904     InitMovingField(jx, jy, MV_DOWN);
13905     Store[jx][jy] = EL_ACID;
13906     ContinueMoving(jx, jy);
13907     BuryPlayer(player);
13908
13909     return MP_DONT_RUN_INTO;
13910   }
13911 #endif
13912
13913 #if USE_FIXED_DONT_RUN_INTO
13914   if (player_can_move && DONT_RUN_INTO(element))
13915   {
13916     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13917
13918     return MP_DONT_RUN_INTO;
13919   }
13920 #endif
13921
13922 #if USE_FIXED_DONT_RUN_INTO
13923   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13924     return MP_NO_ACTION;
13925 #endif
13926
13927 #if !USE_FIXED_DONT_RUN_INTO
13928   element = Feld[x][y];
13929 #endif
13930
13931   collect_count = element_info[element].collect_count_initial;
13932
13933   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13934     return MP_NO_ACTION;
13935
13936   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13937     player_can_move = player_can_move_or_snap;
13938
13939   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13940       game.engine_version >= VERSION_IDENT(2,2,0,0))
13941   {
13942     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13943                                player->index_bit, dig_side);
13944     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13945                                         player->index_bit, dig_side);
13946
13947     if (element == EL_DC_LANDMINE)
13948       Bang(x, y);
13949
13950     if (Feld[x][y] != element)          /* field changed by snapping */
13951       return MP_ACTION;
13952
13953     return MP_NO_ACTION;
13954   }
13955
13956 #if USE_PLAYER_GRAVITY
13957   if (player->gravity && is_player && !player->is_auto_moving &&
13958       canFallDown(player) && move_direction != MV_DOWN &&
13959       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13960     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13961 #else
13962   if (game.gravity && is_player && !player->is_auto_moving &&
13963       canFallDown(player) && move_direction != MV_DOWN &&
13964       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13965     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13966 #endif
13967
13968   if (player_can_move &&
13969       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13970   {
13971     int sound_element = SND_ELEMENT(element);
13972     int sound_action = ACTION_WALKING;
13973
13974     if (IS_RND_GATE(element))
13975     {
13976       if (!player->key[RND_GATE_NR(element)])
13977         return MP_NO_ACTION;
13978     }
13979     else if (IS_RND_GATE_GRAY(element))
13980     {
13981       if (!player->key[RND_GATE_GRAY_NR(element)])
13982         return MP_NO_ACTION;
13983     }
13984     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13985     {
13986       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13987         return MP_NO_ACTION;
13988     }
13989     else if (element == EL_EXIT_OPEN ||
13990              element == EL_EM_EXIT_OPEN ||
13991              element == EL_STEEL_EXIT_OPEN ||
13992              element == EL_EM_STEEL_EXIT_OPEN ||
13993              element == EL_SP_EXIT_OPEN ||
13994              element == EL_SP_EXIT_OPENING)
13995     {
13996       sound_action = ACTION_PASSING;    /* player is passing exit */
13997     }
13998     else if (element == EL_EMPTY)
13999     {
14000       sound_action = ACTION_MOVING;             /* nothing to walk on */
14001     }
14002
14003     /* play sound from background or player, whatever is available */
14004     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14005       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14006     else
14007       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14008   }
14009   else if (player_can_move &&
14010            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14011   {
14012     if (!ACCESS_FROM(element, opposite_direction))
14013       return MP_NO_ACTION;      /* field not accessible from this direction */
14014
14015     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
14016       return MP_NO_ACTION;
14017
14018     if (IS_EM_GATE(element))
14019     {
14020       if (!player->key[EM_GATE_NR(element)])
14021         return MP_NO_ACTION;
14022     }
14023     else if (IS_EM_GATE_GRAY(element))
14024     {
14025       if (!player->key[EM_GATE_GRAY_NR(element)])
14026         return MP_NO_ACTION;
14027     }
14028     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14029     {
14030       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14031         return MP_NO_ACTION;
14032     }
14033     else if (IS_EMC_GATE(element))
14034     {
14035       if (!player->key[EMC_GATE_NR(element)])
14036         return MP_NO_ACTION;
14037     }
14038     else if (IS_EMC_GATE_GRAY(element))
14039     {
14040       if (!player->key[EMC_GATE_GRAY_NR(element)])
14041         return MP_NO_ACTION;
14042     }
14043     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14044     {
14045       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14046         return MP_NO_ACTION;
14047     }
14048     else if (element == EL_DC_GATE_WHITE ||
14049              element == EL_DC_GATE_WHITE_GRAY ||
14050              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14051     {
14052       if (player->num_white_keys == 0)
14053         return MP_NO_ACTION;
14054
14055       player->num_white_keys--;
14056     }
14057     else if (IS_SP_PORT(element))
14058     {
14059       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14060           element == EL_SP_GRAVITY_PORT_RIGHT ||
14061           element == EL_SP_GRAVITY_PORT_UP ||
14062           element == EL_SP_GRAVITY_PORT_DOWN)
14063 #if USE_PLAYER_GRAVITY
14064         player->gravity = !player->gravity;
14065 #else
14066         game.gravity = !game.gravity;
14067 #endif
14068       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14069                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14070                element == EL_SP_GRAVITY_ON_PORT_UP ||
14071                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14072 #if USE_PLAYER_GRAVITY
14073         player->gravity = TRUE;
14074 #else
14075         game.gravity = TRUE;
14076 #endif
14077       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14078                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14079                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14080                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14081 #if USE_PLAYER_GRAVITY
14082         player->gravity = FALSE;
14083 #else
14084         game.gravity = FALSE;
14085 #endif
14086     }
14087
14088     /* automatically move to the next field with double speed */
14089     player->programmed_action = move_direction;
14090
14091     if (player->move_delay_reset_counter == 0)
14092     {
14093       player->move_delay_reset_counter = 2;     /* two double speed steps */
14094
14095       DOUBLE_PLAYER_SPEED(player);
14096     }
14097
14098     PlayLevelSoundAction(x, y, ACTION_PASSING);
14099   }
14100   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14101   {
14102     RemoveField(x, y);
14103
14104     if (mode != DF_SNAP)
14105     {
14106       GfxElement[x][y] = GFX_ELEMENT(element);
14107       player->is_digging = TRUE;
14108     }
14109
14110     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14111
14112     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14113                                         player->index_bit, dig_side);
14114
14115     if (mode == DF_SNAP)
14116     {
14117 #if USE_NEW_SNAP_DELAY
14118       if (level.block_snap_field)
14119         setFieldForSnapping(x, y, element, move_direction);
14120       else
14121         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14122 #else
14123       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14124 #endif
14125
14126       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14127                                           player->index_bit, dig_side);
14128     }
14129   }
14130   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14131   {
14132     RemoveField(x, y);
14133
14134     if (is_player && mode != DF_SNAP)
14135     {
14136       GfxElement[x][y] = element;
14137       player->is_collecting = TRUE;
14138     }
14139
14140     if (element == EL_SPEED_PILL)
14141     {
14142       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14143     }
14144     else if (element == EL_EXTRA_TIME && level.time > 0)
14145     {
14146       TimeLeft += level.extra_time;
14147
14148 #if 1
14149       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14150
14151       DisplayGameControlValues();
14152 #else
14153       DrawGameValue_Time(TimeLeft);
14154 #endif
14155     }
14156     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14157     {
14158       player->shield_normal_time_left += level.shield_normal_time;
14159       if (element == EL_SHIELD_DEADLY)
14160         player->shield_deadly_time_left += level.shield_deadly_time;
14161     }
14162     else if (element == EL_DYNAMITE ||
14163              element == EL_EM_DYNAMITE ||
14164              element == EL_SP_DISK_RED)
14165     {
14166       if (player->inventory_size < MAX_INVENTORY_SIZE)
14167         player->inventory_element[player->inventory_size++] = element;
14168
14169       DrawGameDoorValues();
14170     }
14171     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14172     {
14173       player->dynabomb_count++;
14174       player->dynabombs_left++;
14175     }
14176     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14177     {
14178       player->dynabomb_size++;
14179     }
14180     else if (element == EL_DYNABOMB_INCREASE_POWER)
14181     {
14182       player->dynabomb_xl = TRUE;
14183     }
14184     else if (IS_KEY(element))
14185     {
14186       player->key[KEY_NR(element)] = TRUE;
14187
14188       DrawGameDoorValues();
14189     }
14190     else if (element == EL_DC_KEY_WHITE)
14191     {
14192       player->num_white_keys++;
14193
14194       /* display white keys? */
14195       /* DrawGameDoorValues(); */
14196     }
14197     else if (IS_ENVELOPE(element))
14198     {
14199       player->show_envelope = element;
14200     }
14201     else if (element == EL_EMC_LENSES)
14202     {
14203       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14204
14205       RedrawAllInvisibleElementsForLenses();
14206     }
14207     else if (element == EL_EMC_MAGNIFIER)
14208     {
14209       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14210
14211       RedrawAllInvisibleElementsForMagnifier();
14212     }
14213     else if (IS_DROPPABLE(element) ||
14214              IS_THROWABLE(element))     /* can be collected and dropped */
14215     {
14216       int i;
14217
14218       if (collect_count == 0)
14219         player->inventory_infinite_element = element;
14220       else
14221         for (i = 0; i < collect_count; i++)
14222           if (player->inventory_size < MAX_INVENTORY_SIZE)
14223             player->inventory_element[player->inventory_size++] = element;
14224
14225       DrawGameDoorValues();
14226     }
14227     else if (collect_count > 0)
14228     {
14229       local_player->gems_still_needed -= collect_count;
14230       if (local_player->gems_still_needed < 0)
14231         local_player->gems_still_needed = 0;
14232
14233 #if 1
14234       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
14235
14236       DisplayGameControlValues();
14237 #else
14238       DrawGameValue_Emeralds(local_player->gems_still_needed);
14239 #endif
14240     }
14241
14242     RaiseScoreElement(element);
14243     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14244
14245     if (is_player)
14246       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14247                                           player->index_bit, dig_side);
14248
14249     if (mode == DF_SNAP)
14250     {
14251 #if USE_NEW_SNAP_DELAY
14252       if (level.block_snap_field)
14253         setFieldForSnapping(x, y, element, move_direction);
14254       else
14255         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
14256 #else
14257       TestIfElementTouchesCustomElement(x, y);          /* for empty space */
14258 #endif
14259
14260       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14261                                           player->index_bit, dig_side);
14262     }
14263   }
14264   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14265   {
14266     if (mode == DF_SNAP && element != EL_BD_ROCK)
14267       return MP_NO_ACTION;
14268
14269     if (CAN_FALL(element) && dy)
14270       return MP_NO_ACTION;
14271
14272     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14273         !(element == EL_SPRING && level.use_spring_bug))
14274       return MP_NO_ACTION;
14275
14276     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14277         ((move_direction & MV_VERTICAL &&
14278           ((element_info[element].move_pattern & MV_LEFT &&
14279             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14280            (element_info[element].move_pattern & MV_RIGHT &&
14281             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14282          (move_direction & MV_HORIZONTAL &&
14283           ((element_info[element].move_pattern & MV_UP &&
14284             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14285            (element_info[element].move_pattern & MV_DOWN &&
14286             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14287       return MP_NO_ACTION;
14288
14289     /* do not push elements already moving away faster than player */
14290     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14291         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14292       return MP_NO_ACTION;
14293
14294     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14295     {
14296       if (player->push_delay_value == -1 || !player_was_pushing)
14297         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14298     }
14299     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14300     {
14301       if (player->push_delay_value == -1)
14302         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14303     }
14304     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14305     {
14306       if (!player->is_pushing)
14307         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14308     }
14309
14310     player->is_pushing = TRUE;
14311     player->is_active = TRUE;
14312
14313     if (!(IN_LEV_FIELD(nextx, nexty) &&
14314           (IS_FREE(nextx, nexty) ||
14315            (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY &&
14316             IS_SB_ELEMENT(element)))))
14317       return MP_NO_ACTION;
14318
14319     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14320       return MP_NO_ACTION;
14321
14322     if (player->push_delay == -1)       /* new pushing; restart delay */
14323       player->push_delay = 0;
14324
14325     if (player->push_delay < player->push_delay_value &&
14326         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14327         element != EL_SPRING && element != EL_BALLOON)
14328     {
14329       /* make sure that there is no move delay before next try to push */
14330       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14331         player->move_delay = 0;
14332
14333       return MP_NO_ACTION;
14334     }
14335
14336     if (IS_SB_ELEMENT(element))
14337     {
14338       if (element == EL_SOKOBAN_FIELD_FULL)
14339       {
14340         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14341         local_player->sokobanfields_still_needed++;
14342       }
14343
14344       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14345       {
14346         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14347         local_player->sokobanfields_still_needed--;
14348       }
14349
14350       Feld[x][y] = EL_SOKOBAN_OBJECT;
14351
14352       if (Back[x][y] == Back[nextx][nexty])
14353         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14354       else if (Back[x][y] != 0)
14355         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14356                                     ACTION_EMPTYING);
14357       else
14358         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14359                                     ACTION_FILLING);
14360
14361       if (local_player->sokobanfields_still_needed == 0 &&
14362           game.emulation == EMU_SOKOBAN)
14363       {
14364         PlayerWins(player);
14365
14366         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14367       }
14368     }
14369     else
14370       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14371
14372     InitMovingField(x, y, move_direction);
14373     GfxAction[x][y] = ACTION_PUSHING;
14374
14375     if (mode == DF_SNAP)
14376       ContinueMoving(x, y);
14377     else
14378       MovPos[x][y] = (dx != 0 ? dx : dy);
14379
14380     Pushed[x][y] = TRUE;
14381     Pushed[nextx][nexty] = TRUE;
14382
14383     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14384       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14385     else
14386       player->push_delay_value = -1;    /* get new value later */
14387
14388     /* check for element change _after_ element has been pushed */
14389     if (game.use_change_when_pushing_bug)
14390     {
14391       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14392                                  player->index_bit, dig_side);
14393       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14394                                           player->index_bit, dig_side);
14395     }
14396   }
14397   else if (IS_SWITCHABLE(element))
14398   {
14399     if (PLAYER_SWITCHING(player, x, y))
14400     {
14401       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14402                                           player->index_bit, dig_side);
14403
14404       return MP_ACTION;
14405     }
14406
14407     player->is_switching = TRUE;
14408     player->switch_x = x;
14409     player->switch_y = y;
14410
14411     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14412
14413     if (element == EL_ROBOT_WHEEL)
14414     {
14415       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14416       ZX = x;
14417       ZY = y;
14418
14419       game.robot_wheel_active = TRUE;
14420
14421       DrawLevelField(x, y);
14422     }
14423     else if (element == EL_SP_TERMINAL)
14424     {
14425       int xx, yy;
14426
14427       SCAN_PLAYFIELD(xx, yy)
14428       {
14429         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
14430           Bang(xx, yy);
14431         else if (Feld[xx][yy] == EL_SP_TERMINAL)
14432           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14433       }
14434     }
14435     else if (IS_BELT_SWITCH(element))
14436     {
14437       ToggleBeltSwitch(x, y);
14438     }
14439     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14440              element == EL_SWITCHGATE_SWITCH_DOWN ||
14441              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14442              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14443     {
14444       ToggleSwitchgateSwitch(x, y);
14445     }
14446     else if (element == EL_LIGHT_SWITCH ||
14447              element == EL_LIGHT_SWITCH_ACTIVE)
14448     {
14449       ToggleLightSwitch(x, y);
14450     }
14451     else if (element == EL_TIMEGATE_SWITCH ||
14452              element == EL_DC_TIMEGATE_SWITCH)
14453     {
14454       ActivateTimegateSwitch(x, y);
14455     }
14456     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14457              element == EL_BALLOON_SWITCH_RIGHT ||
14458              element == EL_BALLOON_SWITCH_UP    ||
14459              element == EL_BALLOON_SWITCH_DOWN  ||
14460              element == EL_BALLOON_SWITCH_NONE  ||
14461              element == EL_BALLOON_SWITCH_ANY)
14462     {
14463       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14464                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14465                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14466                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14467                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14468                              move_direction);
14469     }
14470     else if (element == EL_LAMP)
14471     {
14472       Feld[x][y] = EL_LAMP_ACTIVE;
14473       local_player->lights_still_needed--;
14474
14475       ResetGfxAnimation(x, y);
14476       DrawLevelField(x, y);
14477     }
14478     else if (element == EL_TIME_ORB_FULL)
14479     {
14480       Feld[x][y] = EL_TIME_ORB_EMPTY;
14481
14482       if (level.time > 0 || level.use_time_orb_bug)
14483       {
14484         TimeLeft += level.time_orb_time;
14485
14486 #if 1
14487         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14488
14489         DisplayGameControlValues();
14490 #else
14491         DrawGameValue_Time(TimeLeft);
14492 #endif
14493       }
14494
14495       ResetGfxAnimation(x, y);
14496       DrawLevelField(x, y);
14497     }
14498     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14499              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14500     {
14501       int xx, yy;
14502
14503       game.ball_state = !game.ball_state;
14504
14505       SCAN_PLAYFIELD(xx, yy)
14506       {
14507         int e = Feld[xx][yy];
14508
14509         if (game.ball_state)
14510         {
14511           if (e == EL_EMC_MAGIC_BALL)
14512             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14513           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14514             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14515         }
14516         else
14517         {
14518           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14519             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14520           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14521             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14522         }
14523       }
14524     }
14525
14526     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14527                                         player->index_bit, dig_side);
14528
14529     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14530                                         player->index_bit, dig_side);
14531
14532     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14533                                         player->index_bit, dig_side);
14534
14535     return MP_ACTION;
14536   }
14537   else
14538   {
14539     if (!PLAYER_SWITCHING(player, x, y))
14540     {
14541       player->is_switching = TRUE;
14542       player->switch_x = x;
14543       player->switch_y = y;
14544
14545       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14546                                  player->index_bit, dig_side);
14547       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14548                                           player->index_bit, dig_side);
14549
14550       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14551                                  player->index_bit, dig_side);
14552       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14553                                           player->index_bit, dig_side);
14554     }
14555
14556     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14557                                player->index_bit, dig_side);
14558     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14559                                         player->index_bit, dig_side);
14560
14561     return MP_NO_ACTION;
14562   }
14563
14564   player->push_delay = -1;
14565
14566   if (is_player)                /* function can also be called by EL_PENGUIN */
14567   {
14568     if (Feld[x][y] != element)          /* really digged/collected something */
14569     {
14570       player->is_collecting = !player->is_digging;
14571       player->is_active = TRUE;
14572     }
14573   }
14574
14575   return MP_MOVING;
14576 }
14577
14578 boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14579 {
14580   int jx = player->jx, jy = player->jy;
14581   int x = jx + dx, y = jy + dy;
14582   int snap_direction = (dx == -1 ? MV_LEFT  :
14583                         dx == +1 ? MV_RIGHT :
14584                         dy == -1 ? MV_UP    :
14585                         dy == +1 ? MV_DOWN  : MV_NONE);
14586   boolean can_continue_snapping = (level.continuous_snapping &&
14587                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14588
14589   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14590     return FALSE;
14591
14592   if (!player->active || !IN_LEV_FIELD(x, y))
14593     return FALSE;
14594
14595   if (dx && dy)
14596     return FALSE;
14597
14598   if (!dx && !dy)
14599   {
14600     if (player->MovPos == 0)
14601       player->is_pushing = FALSE;
14602
14603     player->is_snapping = FALSE;
14604
14605     if (player->MovPos == 0)
14606     {
14607       player->is_moving = FALSE;
14608       player->is_digging = FALSE;
14609       player->is_collecting = FALSE;
14610     }
14611
14612     return FALSE;
14613   }
14614
14615 #if USE_NEW_CONTINUOUS_SNAPPING
14616   /* prevent snapping with already pressed snap key when not allowed */
14617   if (player->is_snapping && !can_continue_snapping)
14618     return FALSE;
14619 #else
14620   if (player->is_snapping)
14621     return FALSE;
14622 #endif
14623
14624   player->MovDir = snap_direction;
14625
14626   if (player->MovPos == 0)
14627   {
14628     player->is_moving = FALSE;
14629     player->is_digging = FALSE;
14630     player->is_collecting = FALSE;
14631   }
14632
14633   player->is_dropping = FALSE;
14634   player->is_dropping_pressed = FALSE;
14635   player->drop_pressed_delay = 0;
14636
14637   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14638     return FALSE;
14639
14640   player->is_snapping = TRUE;
14641   player->is_active = TRUE;
14642
14643   if (player->MovPos == 0)
14644   {
14645     player->is_moving = FALSE;
14646     player->is_digging = FALSE;
14647     player->is_collecting = FALSE;
14648   }
14649
14650   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
14651     DrawLevelField(player->last_jx, player->last_jy);
14652
14653   DrawLevelField(x, y);
14654
14655   return TRUE;
14656 }
14657
14658 boolean DropElement(struct PlayerInfo *player)
14659 {
14660   int old_element, new_element;
14661   int dropx = player->jx, dropy = player->jy;
14662   int drop_direction = player->MovDir;
14663   int drop_side = drop_direction;
14664 #if 1
14665   int drop_element = get_next_dropped_element(player);
14666 #else
14667   int drop_element = (player->inventory_size > 0 ?
14668                       player->inventory_element[player->inventory_size - 1] :
14669                       player->inventory_infinite_element != EL_UNDEFINED ?
14670                       player->inventory_infinite_element :
14671                       player->dynabombs_left > 0 ?
14672                       EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
14673                       EL_UNDEFINED);
14674 #endif
14675
14676   player->is_dropping_pressed = TRUE;
14677
14678   /* do not drop an element on top of another element; when holding drop key
14679      pressed without moving, dropped element must move away before the next
14680      element can be dropped (this is especially important if the next element
14681      is dynamite, which can be placed on background for historical reasons) */
14682   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14683     return MP_ACTION;
14684
14685   if (IS_THROWABLE(drop_element))
14686   {
14687     dropx += GET_DX_FROM_DIR(drop_direction);
14688     dropy += GET_DY_FROM_DIR(drop_direction);
14689
14690     if (!IN_LEV_FIELD(dropx, dropy))
14691       return FALSE;
14692   }
14693
14694   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14695   new_element = drop_element;           /* default: no change when dropping */
14696
14697   /* check if player is active, not moving and ready to drop */
14698   if (!player->active || player->MovPos || player->drop_delay > 0)
14699     return FALSE;
14700
14701   /* check if player has anything that can be dropped */
14702   if (new_element == EL_UNDEFINED)
14703     return FALSE;
14704
14705   /* check if drop key was pressed long enough for EM style dynamite */
14706   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14707     return FALSE;
14708
14709   /* check if anything can be dropped at the current position */
14710   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14711     return FALSE;
14712
14713   /* collected custom elements can only be dropped on empty fields */
14714   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14715     return FALSE;
14716
14717   if (old_element != EL_EMPTY)
14718     Back[dropx][dropy] = old_element;   /* store old element on this field */
14719
14720   ResetGfxAnimation(dropx, dropy);
14721   ResetRandomAnimationValue(dropx, dropy);
14722
14723   if (player->inventory_size > 0 ||
14724       player->inventory_infinite_element != EL_UNDEFINED)
14725   {
14726     if (player->inventory_size > 0)
14727     {
14728       player->inventory_size--;
14729
14730       DrawGameDoorValues();
14731
14732       if (new_element == EL_DYNAMITE)
14733         new_element = EL_DYNAMITE_ACTIVE;
14734       else if (new_element == EL_EM_DYNAMITE)
14735         new_element = EL_EM_DYNAMITE_ACTIVE;
14736       else if (new_element == EL_SP_DISK_RED)
14737         new_element = EL_SP_DISK_RED_ACTIVE;
14738     }
14739
14740     Feld[dropx][dropy] = new_element;
14741
14742     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14743       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14744                           el2img(Feld[dropx][dropy]), 0);
14745
14746     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14747
14748     /* needed if previous element just changed to "empty" in the last frame */
14749     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14750
14751     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14752                                player->index_bit, drop_side);
14753     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14754                                         CE_PLAYER_DROPS_X,
14755                                         player->index_bit, drop_side);
14756
14757     TestIfElementTouchesCustomElement(dropx, dropy);
14758   }
14759   else          /* player is dropping a dyna bomb */
14760   {
14761     player->dynabombs_left--;
14762
14763     Feld[dropx][dropy] = new_element;
14764
14765     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14766       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14767                           el2img(Feld[dropx][dropy]), 0);
14768
14769     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14770   }
14771
14772   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14773     InitField_WithBug1(dropx, dropy, FALSE);
14774
14775   new_element = Feld[dropx][dropy];     /* element might have changed */
14776
14777   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14778       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14779   {
14780     int move_direction, nextx, nexty;
14781
14782     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14783       MovDir[dropx][dropy] = drop_direction;
14784
14785     move_direction = MovDir[dropx][dropy];
14786     nextx = dropx + GET_DX_FROM_DIR(move_direction);
14787     nexty = dropy + GET_DY_FROM_DIR(move_direction);
14788
14789     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14790
14791 #if USE_FIX_IMPACT_COLLISION
14792     /* do not cause impact style collision by dropping elements that can fall */
14793     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14794 #else
14795     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14796 #endif
14797   }
14798
14799   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14800   player->is_dropping = TRUE;
14801
14802   player->drop_pressed_delay = 0;
14803   player->is_dropping_pressed = FALSE;
14804
14805   player->drop_x = dropx;
14806   player->drop_y = dropy;
14807
14808   return TRUE;
14809 }
14810
14811 /* ------------------------------------------------------------------------- */
14812 /* game sound playing functions                                              */
14813 /* ------------------------------------------------------------------------- */
14814
14815 static int *loop_sound_frame = NULL;
14816 static int *loop_sound_volume = NULL;
14817
14818 void InitPlayLevelSound()
14819 {
14820   int num_sounds = getSoundListSize();
14821
14822   checked_free(loop_sound_frame);
14823   checked_free(loop_sound_volume);
14824
14825   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14826   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14827 }
14828
14829 static void PlayLevelSound(int x, int y, int nr)
14830 {
14831   int sx = SCREENX(x), sy = SCREENY(y);
14832   int volume, stereo_position;
14833   int max_distance = 8;
14834   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14835
14836   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14837       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14838     return;
14839
14840   if (!IN_LEV_FIELD(x, y) ||
14841       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14842       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14843     return;
14844
14845   volume = SOUND_MAX_VOLUME;
14846
14847   if (!IN_SCR_FIELD(sx, sy))
14848   {
14849     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14850     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14851
14852     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14853   }
14854
14855   stereo_position = (SOUND_MAX_LEFT +
14856                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14857                      (SCR_FIELDX + 2 * max_distance));
14858
14859   if (IS_LOOP_SOUND(nr))
14860   {
14861     /* This assures that quieter loop sounds do not overwrite louder ones,
14862        while restarting sound volume comparison with each new game frame. */
14863
14864     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14865       return;
14866
14867     loop_sound_volume[nr] = volume;
14868     loop_sound_frame[nr] = FrameCounter;
14869   }
14870
14871   PlaySoundExt(nr, volume, stereo_position, type);
14872 }
14873
14874 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14875 {
14876   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14877                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14878                  y < LEVELY(BY1) ? LEVELY(BY1) :
14879                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14880                  sound_action);
14881 }
14882
14883 static void PlayLevelSoundAction(int x, int y, int action)
14884 {
14885   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14886 }
14887
14888 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14889 {
14890   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14891
14892   if (sound_effect != SND_UNDEFINED)
14893     PlayLevelSound(x, y, sound_effect);
14894 }
14895
14896 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14897                                               int action)
14898 {
14899   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14900
14901   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14902     PlayLevelSound(x, y, sound_effect);
14903 }
14904
14905 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14906 {
14907   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14908
14909   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14910     PlayLevelSound(x, y, sound_effect);
14911 }
14912
14913 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14914 {
14915   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14916
14917   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14918     StopSound(sound_effect);
14919 }
14920
14921 static void PlayLevelMusic()
14922 {
14923   if (levelset.music[level_nr] != MUS_UNDEFINED)
14924     PlayMusic(levelset.music[level_nr]);        /* from config file */
14925   else
14926     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14927 }
14928
14929 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14930 {
14931   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14932   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14933   int x = xx - 1 - offset;
14934   int y = yy - 1 - offset;
14935
14936   switch (sample)
14937   {
14938     case SAMPLE_blank:
14939       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14940       break;
14941
14942     case SAMPLE_roll:
14943       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14944       break;
14945
14946     case SAMPLE_stone:
14947       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14948       break;
14949
14950     case SAMPLE_nut:
14951       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14952       break;
14953
14954     case SAMPLE_crack:
14955       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14956       break;
14957
14958     case SAMPLE_bug:
14959       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14960       break;
14961
14962     case SAMPLE_tank:
14963       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14964       break;
14965
14966     case SAMPLE_android_clone:
14967       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14968       break;
14969
14970     case SAMPLE_android_move:
14971       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14972       break;
14973
14974     case SAMPLE_spring:
14975       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14976       break;
14977
14978     case SAMPLE_slurp:
14979       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14980       break;
14981
14982     case SAMPLE_eater:
14983       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14984       break;
14985
14986     case SAMPLE_eater_eat:
14987       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14988       break;
14989
14990     case SAMPLE_alien:
14991       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14992       break;
14993
14994     case SAMPLE_collect:
14995       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14996       break;
14997
14998     case SAMPLE_diamond:
14999       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15000       break;
15001
15002     case SAMPLE_squash:
15003       /* !!! CHECK THIS !!! */
15004 #if 1
15005       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15006 #else
15007       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15008 #endif
15009       break;
15010
15011     case SAMPLE_wonderfall:
15012       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15013       break;
15014
15015     case SAMPLE_drip:
15016       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15017       break;
15018
15019     case SAMPLE_push:
15020       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15021       break;
15022
15023     case SAMPLE_dirt:
15024       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15025       break;
15026
15027     case SAMPLE_acid:
15028       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15029       break;
15030
15031     case SAMPLE_ball:
15032       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15033       break;
15034
15035     case SAMPLE_grow:
15036       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15037       break;
15038
15039     case SAMPLE_wonder:
15040       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15041       break;
15042
15043     case SAMPLE_door:
15044       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15045       break;
15046
15047     case SAMPLE_exit_open:
15048       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15049       break;
15050
15051     case SAMPLE_exit_leave:
15052       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15053       break;
15054
15055     case SAMPLE_dynamite:
15056       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15057       break;
15058
15059     case SAMPLE_tick:
15060       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15061       break;
15062
15063     case SAMPLE_press:
15064       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15065       break;
15066
15067     case SAMPLE_wheel:
15068       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15069       break;
15070
15071     case SAMPLE_boom:
15072       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15073       break;
15074
15075     case SAMPLE_die:
15076       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15077       break;
15078
15079     case SAMPLE_time:
15080       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15081       break;
15082
15083     default:
15084       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15085       break;
15086   }
15087 }
15088
15089 #if 0
15090 void ChangeTime(int value)
15091 {
15092   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
15093
15094   *time += value;
15095
15096   /* EMC game engine uses value from time counter of RND game engine */
15097   level.native_em_level->lev->time = *time;
15098
15099   DrawGameValue_Time(*time);
15100 }
15101
15102 void RaiseScore(int value)
15103 {
15104   /* EMC game engine and RND game engine have separate score counters */
15105   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
15106                 &level.native_em_level->lev->score : &local_player->score);
15107
15108   *score += value;
15109
15110   DrawGameValue_Score(*score);
15111 }
15112 #endif
15113
15114 void RaiseScore(int value)
15115 {
15116   local_player->score += value;
15117
15118 #if 1
15119   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
15120
15121   DisplayGameControlValues();
15122 #else
15123   DrawGameValue_Score(local_player->score);
15124 #endif
15125 }
15126
15127 void RaiseScoreElement(int element)
15128 {
15129   switch (element)
15130   {
15131     case EL_EMERALD:
15132     case EL_BD_DIAMOND:
15133     case EL_EMERALD_YELLOW:
15134     case EL_EMERALD_RED:
15135     case EL_EMERALD_PURPLE:
15136     case EL_SP_INFOTRON:
15137       RaiseScore(level.score[SC_EMERALD]);
15138       break;
15139     case EL_DIAMOND:
15140       RaiseScore(level.score[SC_DIAMOND]);
15141       break;
15142     case EL_CRYSTAL:
15143       RaiseScore(level.score[SC_CRYSTAL]);
15144       break;
15145     case EL_PEARL:
15146       RaiseScore(level.score[SC_PEARL]);
15147       break;
15148     case EL_BUG:
15149     case EL_BD_BUTTERFLY:
15150     case EL_SP_ELECTRON:
15151       RaiseScore(level.score[SC_BUG]);
15152       break;
15153     case EL_SPACESHIP:
15154     case EL_BD_FIREFLY:
15155     case EL_SP_SNIKSNAK:
15156       RaiseScore(level.score[SC_SPACESHIP]);
15157       break;
15158     case EL_YAMYAM:
15159     case EL_DARK_YAMYAM:
15160       RaiseScore(level.score[SC_YAMYAM]);
15161       break;
15162     case EL_ROBOT:
15163       RaiseScore(level.score[SC_ROBOT]);
15164       break;
15165     case EL_PACMAN:
15166       RaiseScore(level.score[SC_PACMAN]);
15167       break;
15168     case EL_NUT:
15169       RaiseScore(level.score[SC_NUT]);
15170       break;
15171     case EL_DYNAMITE:
15172     case EL_EM_DYNAMITE:
15173     case EL_SP_DISK_RED:
15174     case EL_DYNABOMB_INCREASE_NUMBER:
15175     case EL_DYNABOMB_INCREASE_SIZE:
15176     case EL_DYNABOMB_INCREASE_POWER:
15177       RaiseScore(level.score[SC_DYNAMITE]);
15178       break;
15179     case EL_SHIELD_NORMAL:
15180     case EL_SHIELD_DEADLY:
15181       RaiseScore(level.score[SC_SHIELD]);
15182       break;
15183     case EL_EXTRA_TIME:
15184       RaiseScore(level.extra_time_score);
15185       break;
15186     case EL_KEY_1:
15187     case EL_KEY_2:
15188     case EL_KEY_3:
15189     case EL_KEY_4:
15190     case EL_EM_KEY_1:
15191     case EL_EM_KEY_2:
15192     case EL_EM_KEY_3:
15193     case EL_EM_KEY_4:
15194     case EL_EMC_KEY_5:
15195     case EL_EMC_KEY_6:
15196     case EL_EMC_KEY_7:
15197     case EL_EMC_KEY_8:
15198     case EL_DC_KEY_WHITE:
15199       RaiseScore(level.score[SC_KEY]);
15200       break;
15201     default:
15202       RaiseScore(element_info[element].collect_score);
15203       break;
15204   }
15205 }
15206
15207 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15208 {
15209   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15210   {
15211 #if defined(NETWORK_AVALIABLE)
15212     if (options.network)
15213       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15214     else
15215 #endif
15216     {
15217       if (quick_quit)
15218       {
15219 #if 1
15220
15221 #if 1
15222         FadeSkipNextFadeIn();
15223 #else
15224         fading = fading_none;
15225 #endif
15226
15227 #else
15228         OpenDoor(DOOR_CLOSE_1);
15229 #endif
15230
15231         game_status = GAME_MODE_MAIN;
15232
15233 #if 1
15234         DrawAndFadeInMainMenu(REDRAW_FIELD);
15235 #else
15236         DrawMainMenu();
15237 #endif
15238       }
15239       else
15240       {
15241 #if 0
15242         FadeOut(REDRAW_FIELD);
15243 #endif
15244
15245         game_status = GAME_MODE_MAIN;
15246
15247         DrawAndFadeInMainMenu(REDRAW_FIELD);
15248       }
15249     }
15250   }
15251   else          /* continue playing the game */
15252   {
15253     if (tape.playing && tape.deactivate_display)
15254       TapeDeactivateDisplayOff(TRUE);
15255
15256     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15257
15258     if (tape.playing && tape.deactivate_display)
15259       TapeDeactivateDisplayOn();
15260   }
15261 }
15262
15263 void RequestQuitGame(boolean ask_if_really_quit)
15264 {
15265   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15266   boolean skip_request = AllPlayersGone || quick_quit;
15267
15268   RequestQuitGameExt(skip_request, quick_quit,
15269                      "Do you really want to quit the game ?");
15270 }
15271
15272
15273 /* ------------------------------------------------------------------------- */
15274 /* random generator functions                                                */
15275 /* ------------------------------------------------------------------------- */
15276
15277 unsigned int InitEngineRandom_RND(long seed)
15278 {
15279   game.num_random_calls = 0;
15280
15281 #if 0
15282   unsigned int rnd_seed = InitEngineRandom(seed);
15283
15284   printf("::: START RND: %d\n", rnd_seed);
15285
15286   return rnd_seed;
15287 #else
15288
15289   return InitEngineRandom(seed);
15290
15291 #endif
15292
15293 }
15294
15295 unsigned int RND(int max)
15296 {
15297   if (max > 0)
15298   {
15299     game.num_random_calls++;
15300
15301     return GetEngineRandom(max);
15302   }
15303
15304   return 0;
15305 }
15306
15307
15308 /* ------------------------------------------------------------------------- */
15309 /* game engine snapshot handling functions                                   */
15310 /* ------------------------------------------------------------------------- */
15311
15312 #define ARGS_ADDRESS_AND_SIZEOF(x)              (&(x)), (sizeof(x))
15313
15314 struct EngineSnapshotInfo
15315 {
15316   /* runtime values for custom element collect score */
15317   int collect_score[NUM_CUSTOM_ELEMENTS];
15318
15319   /* runtime values for group element choice position */
15320   int choice_pos[NUM_GROUP_ELEMENTS];
15321
15322   /* runtime values for belt position animations */
15323   int belt_graphic[4 * NUM_BELT_PARTS];
15324   int belt_anim_mode[4 * NUM_BELT_PARTS];
15325 };
15326
15327 struct EngineSnapshotNodeInfo
15328 {
15329   void *buffer_orig;
15330   void *buffer_copy;
15331   int size;
15332 };
15333
15334 static struct EngineSnapshotInfo engine_snapshot_rnd;
15335 static ListNode *engine_snapshot_list = NULL;
15336 static char *snapshot_level_identifier = NULL;
15337 static int snapshot_level_nr = -1;
15338
15339 void FreeEngineSnapshot()
15340 {
15341   while (engine_snapshot_list != NULL)
15342     deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
15343                        checked_free);
15344
15345   setString(&snapshot_level_identifier, NULL);
15346   snapshot_level_nr = -1;
15347 }
15348
15349 static void SaveEngineSnapshotValues_RND()
15350 {
15351   static int belt_base_active_element[4] =
15352   {
15353     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15354     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15355     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15356     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15357   };
15358   int i, j;
15359
15360   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15361   {
15362     int element = EL_CUSTOM_START + i;
15363
15364     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15365   }
15366
15367   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15368   {
15369     int element = EL_GROUP_START + i;
15370
15371     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15372   }
15373
15374   for (i = 0; i < 4; i++)
15375   {
15376     for (j = 0; j < NUM_BELT_PARTS; j++)
15377     {
15378       int element = belt_base_active_element[i] + j;
15379       int graphic = el2img(element);
15380       int anim_mode = graphic_info[graphic].anim_mode;
15381
15382       engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
15383       engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
15384     }
15385   }
15386 }
15387
15388 static void LoadEngineSnapshotValues_RND()
15389 {
15390   unsigned long num_random_calls = game.num_random_calls;
15391   int i, j;
15392
15393   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15394   {
15395     int element = EL_CUSTOM_START + i;
15396
15397     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15398   }
15399
15400   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15401   {
15402     int element = EL_GROUP_START + i;
15403
15404     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15405   }
15406
15407   for (i = 0; i < 4; i++)
15408   {
15409     for (j = 0; j < NUM_BELT_PARTS; j++)
15410     {
15411       int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
15412       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
15413
15414       graphic_info[graphic].anim_mode = anim_mode;
15415     }
15416   }
15417
15418   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15419   {
15420     InitRND(tape.random_seed);
15421     for (i = 0; i < num_random_calls; i++)
15422       RND(1);
15423   }
15424
15425   if (game.num_random_calls != num_random_calls)
15426   {
15427     Error(ERR_INFO, "number of random calls out of sync");
15428     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
15429     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
15430     Error(ERR_EXIT, "this should not happen -- please debug");
15431   }
15432 }
15433
15434 static void SaveEngineSnapshotBuffer(void *buffer, int size)
15435 {
15436   struct EngineSnapshotNodeInfo *bi =
15437     checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
15438
15439   bi->buffer_orig = buffer;
15440   bi->buffer_copy = checked_malloc(size);
15441   bi->size = size;
15442
15443   memcpy(bi->buffer_copy, buffer, size);
15444
15445   addNodeToList(&engine_snapshot_list, NULL, bi);
15446 }
15447
15448 void SaveEngineSnapshot()
15449 {
15450   FreeEngineSnapshot();         /* free previous snapshot, if needed */
15451
15452   if (level_editor_test_game)   /* do not save snapshots from editor */
15453     return;
15454
15455   /* copy some special values to a structure better suited for the snapshot */
15456
15457   SaveEngineSnapshotValues_RND();
15458   SaveEngineSnapshotValues_EM();
15459
15460   /* save values stored in special snapshot structure */
15461
15462   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15463   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15464
15465   /* save further RND engine values */
15466
15467   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
15468   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
15469   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
15470
15471   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
15472   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
15473   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
15474   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
15475
15476   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15477   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15478   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15479   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15480   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15481
15482   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15483   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15484   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15485
15486   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15487
15488   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
15489
15490   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15491   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15492
15493   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
15494   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
15495   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
15496   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15497   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15498   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15499   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15500   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
15501   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
15502   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15503   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
15504   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15505   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15506   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15507   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15508   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15509   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
15510   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
15511
15512   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15513   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15514
15515   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15516   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15517   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15518
15519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15520   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15521
15522   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15523   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15524   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15525   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15526   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15527
15528   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15529   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15530
15531   /* save level identification information */
15532
15533   setString(&snapshot_level_identifier, leveldir_current->identifier);
15534   snapshot_level_nr = level_nr;
15535
15536 #if 0
15537   ListNode *node = engine_snapshot_list;
15538   int num_bytes = 0;
15539
15540   while (node != NULL)
15541   {
15542     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15543
15544     node = node->next;
15545   }
15546
15547   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
15548 #endif
15549 }
15550
15551 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
15552 {
15553   memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
15554 }
15555
15556 void LoadEngineSnapshot()
15557 {
15558   ListNode *node = engine_snapshot_list;
15559
15560   if (engine_snapshot_list == NULL)
15561     return;
15562
15563   while (node != NULL)
15564   {
15565     LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
15566
15567     node = node->next;
15568   }
15569
15570   /* restore special values from snapshot structure */
15571
15572   LoadEngineSnapshotValues_RND();
15573   LoadEngineSnapshotValues_EM();
15574 }
15575
15576 boolean CheckEngineSnapshot()
15577 {
15578   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15579           snapshot_level_nr == level_nr);
15580 }
15581
15582
15583 /* ---------- new game button stuff ---------------------------------------- */
15584
15585 /* graphic position values for game buttons */
15586 #define GAME_BUTTON_XSIZE       30
15587 #define GAME_BUTTON_YSIZE       30
15588 #define GAME_BUTTON_XPOS        5
15589 #define GAME_BUTTON_YPOS        215
15590 #define SOUND_BUTTON_XPOS       5
15591 #define SOUND_BUTTON_YPOS       (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
15592
15593 #define GAME_BUTTON_STOP_XPOS   (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15594 #define GAME_BUTTON_PAUSE_XPOS  (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15595 #define GAME_BUTTON_PLAY_XPOS   (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15596 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
15597 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
15598 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
15599
15600 static struct
15601 {
15602   int *x, *y;
15603   int gd_x, gd_y;
15604   int gadget_id;
15605   char *infotext;
15606 } gamebutton_info[NUM_GAME_BUTTONS] =
15607 {
15608 #if 1
15609   {
15610     &game.button.stop.x,        &game.button.stop.y,
15611     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15612     GAME_CTRL_ID_STOP,
15613     "stop game"
15614   },
15615   {
15616     &game.button.pause.x,       &game.button.pause.y,
15617     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15618     GAME_CTRL_ID_PAUSE,
15619     "pause game"
15620   },
15621   {
15622     &game.button.play.x,        &game.button.play.y,
15623     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15624     GAME_CTRL_ID_PLAY,
15625     "play game"
15626   },
15627   {
15628     &game.button.sound_music.x, &game.button.sound_music.y,
15629     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15630     SOUND_CTRL_ID_MUSIC,
15631     "background music on/off"
15632   },
15633   {
15634     &game.button.sound_loops.x, &game.button.sound_loops.y,
15635     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15636     SOUND_CTRL_ID_LOOPS,
15637     "sound loops on/off"
15638   },
15639   {
15640     &game.button.sound_simple.x,&game.button.sound_simple.y,
15641     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15642     SOUND_CTRL_ID_SIMPLE,
15643     "normal sounds on/off"
15644   }
15645 #else
15646   {
15647     GAME_BUTTON_STOP_XPOS,      GAME_BUTTON_YPOS,
15648     GAME_CTRL_ID_STOP,
15649     "stop game"
15650   },
15651   {
15652     GAME_BUTTON_PAUSE_XPOS,     GAME_BUTTON_YPOS,
15653     GAME_CTRL_ID_PAUSE,
15654     "pause game"
15655   },
15656   {
15657     GAME_BUTTON_PLAY_XPOS,      GAME_BUTTON_YPOS,
15658     GAME_CTRL_ID_PLAY,
15659     "play game"
15660   },
15661   {
15662     SOUND_BUTTON_MUSIC_XPOS,    SOUND_BUTTON_YPOS,
15663     SOUND_CTRL_ID_MUSIC,
15664     "background music on/off"
15665   },
15666   {
15667     SOUND_BUTTON_LOOPS_XPOS,    SOUND_BUTTON_YPOS,
15668     SOUND_CTRL_ID_LOOPS,
15669     "sound loops on/off"
15670   },
15671   {
15672     SOUND_BUTTON_SIMPLE_XPOS,   SOUND_BUTTON_YPOS,
15673     SOUND_CTRL_ID_SIMPLE,
15674     "normal sounds on/off"
15675   }
15676 #endif
15677 };
15678
15679 void CreateGameButtons()
15680 {
15681   int i;
15682
15683   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15684   {
15685     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
15686     struct GadgetInfo *gi;
15687     int button_type;
15688     boolean checked;
15689     unsigned long event_mask;
15690     int x, y;
15691     int gd_xoffset, gd_yoffset;
15692     int gd_x1, gd_x2, gd_y1, gd_y2;
15693     int id = i;
15694
15695     x = DX + *gamebutton_info[i].x;
15696     y = DY + *gamebutton_info[i].y;
15697     gd_xoffset = gamebutton_info[i].gd_x;
15698     gd_yoffset = gamebutton_info[i].gd_y;
15699     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
15700     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
15701
15702     if (id == GAME_CTRL_ID_STOP ||
15703         id == GAME_CTRL_ID_PAUSE ||
15704         id == GAME_CTRL_ID_PLAY)
15705     {
15706       button_type = GD_TYPE_NORMAL_BUTTON;
15707       checked = FALSE;
15708       event_mask = GD_EVENT_RELEASED;
15709       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15710       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15711     }
15712     else
15713     {
15714       button_type = GD_TYPE_CHECK_BUTTON;
15715       checked =
15716         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15717          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15718          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15719       event_mask = GD_EVENT_PRESSED;
15720       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
15721       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
15722     }
15723
15724     gi = CreateGadget(GDI_CUSTOM_ID, id,
15725                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15726 #if 1
15727                       GDI_X, x,
15728                       GDI_Y, y,
15729 #else
15730                       GDI_X, DX + gd_xoffset,
15731                       GDI_Y, DY + gd_yoffset,
15732 #endif
15733                       GDI_WIDTH, GAME_BUTTON_XSIZE,
15734                       GDI_HEIGHT, GAME_BUTTON_YSIZE,
15735                       GDI_TYPE, button_type,
15736                       GDI_STATE, GD_BUTTON_UNPRESSED,
15737                       GDI_CHECKED, checked,
15738                       GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
15739                       GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
15740                       GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
15741                       GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
15742                       GDI_DIRECT_DRAW, FALSE,
15743                       GDI_EVENT_MASK, event_mask,
15744                       GDI_CALLBACK_ACTION, HandleGameButtons,
15745                       GDI_END);
15746
15747     if (gi == NULL)
15748       Error(ERR_EXIT, "cannot create gadget");
15749
15750     game_gadget[id] = gi;
15751   }
15752 }
15753
15754 void FreeGameButtons()
15755 {
15756   int i;
15757
15758   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15759     FreeGadget(game_gadget[i]);
15760 }
15761
15762 static void MapGameButtons()
15763 {
15764   int i;
15765
15766   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15767     MapGadget(game_gadget[i]);
15768 }
15769
15770 void UnmapGameButtons()
15771 {
15772   int i;
15773
15774   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15775     UnmapGadget(game_gadget[i]);
15776 }
15777
15778 void RedrawGameButtons()
15779 {
15780   int i;
15781
15782   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15783     RedrawGadget(game_gadget[i]);
15784 }
15785
15786 static void HandleGameButtons(struct GadgetInfo *gi)
15787 {
15788   int id = gi->custom_id;
15789
15790   if (game_status != GAME_MODE_PLAYING)
15791     return;
15792
15793   switch (id)
15794   {
15795     case GAME_CTRL_ID_STOP:
15796       if (tape.playing)
15797         TapeStop();
15798       else
15799         RequestQuitGame(TRUE);
15800       break;
15801
15802     case GAME_CTRL_ID_PAUSE:
15803       if (options.network)
15804       {
15805 #if defined(NETWORK_AVALIABLE)
15806         if (tape.pausing)
15807           SendToServer_ContinuePlaying();
15808         else
15809           SendToServer_PausePlaying();
15810 #endif
15811       }
15812       else
15813         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15814       break;
15815
15816     case GAME_CTRL_ID_PLAY:
15817       if (tape.pausing)
15818       {
15819 #if defined(NETWORK_AVALIABLE)
15820         if (options.network)
15821           SendToServer_ContinuePlaying();
15822         else
15823 #endif
15824         {
15825           tape.pausing = FALSE;
15826           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15827         }
15828       }
15829       break;
15830
15831     case SOUND_CTRL_ID_MUSIC:
15832       if (setup.sound_music)
15833       { 
15834         setup.sound_music = FALSE;
15835         FadeMusic();
15836       }
15837       else if (audio.music_available)
15838       { 
15839         setup.sound = setup.sound_music = TRUE;
15840
15841         SetAudioMode(setup.sound);
15842
15843         PlayLevelMusic();
15844       }
15845       break;
15846
15847     case SOUND_CTRL_ID_LOOPS:
15848       if (setup.sound_loops)
15849         setup.sound_loops = FALSE;
15850       else if (audio.loops_available)
15851       {
15852         setup.sound = setup.sound_loops = TRUE;
15853         SetAudioMode(setup.sound);
15854       }
15855       break;
15856
15857     case SOUND_CTRL_ID_SIMPLE:
15858       if (setup.sound_simple)
15859         setup.sound_simple = FALSE;
15860       else if (audio.sound_available)
15861       {
15862         setup.sound = setup.sound_simple = TRUE;
15863         SetAudioMode(setup.sound);
15864       }
15865       break;
15866
15867     default:
15868       break;
15869   }
15870 }