added generic solution for naming conventions of extra graphic definitions
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
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
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   SetGameStatus(GAME_MODE_PLAYING);
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.collected_item = FALSE;
3044   game.snapshot.mode =
3045     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3046      SNAPSHOT_MODE_EVERY_STEP :
3047      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3048      SNAPSHOT_MODE_EVERY_MOVE :
3049      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3050      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3051 }
3052
3053 int get_num_special_action(int element, int action_first, int action_last)
3054 {
3055   int num_special_action = 0;
3056   int i, j;
3057
3058   for (i = action_first; i <= action_last; i++)
3059   {
3060     boolean found = FALSE;
3061
3062     for (j = 0; j < NUM_DIRECTIONS; j++)
3063       if (el_act_dir2img(element, i, j) !=
3064           el_act_dir2img(element, ACTION_DEFAULT, j))
3065         found = TRUE;
3066
3067     if (found)
3068       num_special_action++;
3069     else
3070       break;
3071   }
3072
3073   return num_special_action;
3074 }
3075
3076
3077 /*
3078   =============================================================================
3079   InitGame()
3080   -----------------------------------------------------------------------------
3081   initialize and start new game
3082   =============================================================================
3083 */
3084
3085 void InitGame()
3086 {
3087   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3088   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3089   int fade_mask = REDRAW_FIELD;
3090
3091   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3092   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3093   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3094   int initial_move_dir = MV_DOWN;
3095   int i, j, x, y;
3096
3097   // required here to update video display before fading (FIX THIS)
3098   DrawMaskedBorder(REDRAW_DOOR_2);
3099
3100   if (!game.restart_level)
3101     CloseDoor(DOOR_CLOSE_1);
3102
3103   SetGameStatus(GAME_MODE_PLAYING);
3104
3105   /* needed if different viewport properties defined for playing */
3106   ChangeViewportPropertiesIfNeeded();
3107
3108   if (level_editor_test_game)
3109     FadeSkipNextFadeIn();
3110   else
3111     FadeSetEnterScreen();
3112
3113   if (CheckIfGlobalBorderHasChanged())
3114     fade_mask = REDRAW_ALL;
3115
3116   FadeSoundsAndMusic();
3117
3118   ExpireSoundLoops(TRUE);
3119
3120   FadeOut(fade_mask);
3121
3122   ClearField();
3123
3124   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3125
3126   DrawCompleteVideoDisplay();
3127
3128   InitGameEngine();
3129   InitGameControlValues();
3130
3131   /* don't play tapes over network */
3132   network_playing = (options.network && !tape.playing);
3133
3134   for (i = 0; i < MAX_PLAYERS; i++)
3135   {
3136     struct PlayerInfo *player = &stored_player[i];
3137
3138     player->index_nr = i;
3139     player->index_bit = (1 << i);
3140     player->element_nr = EL_PLAYER_1 + i;
3141
3142     player->present = FALSE;
3143     player->active = FALSE;
3144     player->mapped = FALSE;
3145
3146     player->killed = FALSE;
3147     player->reanimated = FALSE;
3148
3149     player->action = 0;
3150     player->effective_action = 0;
3151     player->programmed_action = 0;
3152
3153     player->score = 0;
3154     player->score_final = 0;
3155
3156     player->gems_still_needed = level.gems_needed;
3157     player->sokobanfields_still_needed = 0;
3158     player->lights_still_needed = 0;
3159     player->friends_still_needed = 0;
3160
3161     for (j = 0; j < MAX_NUM_KEYS; j++)
3162       player->key[j] = FALSE;
3163
3164     player->num_white_keys = 0;
3165
3166     player->dynabomb_count = 0;
3167     player->dynabomb_size = 1;
3168     player->dynabombs_left = 0;
3169     player->dynabomb_xl = FALSE;
3170
3171     player->MovDir = initial_move_dir;
3172     player->MovPos = 0;
3173     player->GfxPos = 0;
3174     player->GfxDir = initial_move_dir;
3175     player->GfxAction = ACTION_DEFAULT;
3176     player->Frame = 0;
3177     player->StepFrame = 0;
3178
3179     player->initial_element = player->element_nr;
3180     player->artwork_element =
3181       (level.use_artwork_element[i] ? level.artwork_element[i] :
3182        player->element_nr);
3183     player->use_murphy = FALSE;
3184
3185     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3186     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3187
3188     player->gravity = level.initial_player_gravity[i];
3189
3190     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3191
3192     player->actual_frame_counter = 0;
3193
3194     player->step_counter = 0;
3195
3196     player->last_move_dir = initial_move_dir;
3197
3198     player->is_active = FALSE;
3199
3200     player->is_waiting = FALSE;
3201     player->is_moving = FALSE;
3202     player->is_auto_moving = FALSE;
3203     player->is_digging = FALSE;
3204     player->is_snapping = FALSE;
3205     player->is_collecting = FALSE;
3206     player->is_pushing = FALSE;
3207     player->is_switching = FALSE;
3208     player->is_dropping = FALSE;
3209     player->is_dropping_pressed = FALSE;
3210
3211     player->is_bored = FALSE;
3212     player->is_sleeping = FALSE;
3213
3214     player->frame_counter_bored = -1;
3215     player->frame_counter_sleeping = -1;
3216
3217     player->anim_delay_counter = 0;
3218     player->post_delay_counter = 0;
3219
3220     player->dir_waiting = initial_move_dir;
3221     player->action_waiting = ACTION_DEFAULT;
3222     player->last_action_waiting = ACTION_DEFAULT;
3223     player->special_action_bored = ACTION_DEFAULT;
3224     player->special_action_sleeping = ACTION_DEFAULT;
3225
3226     player->switch_x = -1;
3227     player->switch_y = -1;
3228
3229     player->drop_x = -1;
3230     player->drop_y = -1;
3231
3232     player->show_envelope = 0;
3233
3234     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3235
3236     player->push_delay       = -1;      /* initialized when pushing starts */
3237     player->push_delay_value = game.initial_push_delay_value;
3238
3239     player->drop_delay = 0;
3240     player->drop_pressed_delay = 0;
3241
3242     player->last_jx = -1;
3243     player->last_jy = -1;
3244     player->jx = -1;
3245     player->jy = -1;
3246
3247     player->shield_normal_time_left = 0;
3248     player->shield_deadly_time_left = 0;
3249
3250     player->inventory_infinite_element = EL_UNDEFINED;
3251     player->inventory_size = 0;
3252
3253     if (level.use_initial_inventory[i])
3254     {
3255       for (j = 0; j < level.initial_inventory_size[i]; j++)
3256       {
3257         int element = level.initial_inventory_content[i][j];
3258         int collect_count = element_info[element].collect_count_initial;
3259         int k;
3260
3261         if (!IS_CUSTOM_ELEMENT(element))
3262           collect_count = 1;
3263
3264         if (collect_count == 0)
3265           player->inventory_infinite_element = element;
3266         else
3267           for (k = 0; k < collect_count; k++)
3268             if (player->inventory_size < MAX_INVENTORY_SIZE)
3269               player->inventory_element[player->inventory_size++] = element;
3270       }
3271     }
3272
3273     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3274     SnapField(player, 0, 0);
3275
3276     player->LevelSolved = FALSE;
3277     player->GameOver = FALSE;
3278
3279     player->LevelSolved_GameWon = FALSE;
3280     player->LevelSolved_GameEnd = FALSE;
3281     player->LevelSolved_PanelOff = FALSE;
3282     player->LevelSolved_SaveTape = FALSE;
3283     player->LevelSolved_SaveScore = FALSE;
3284     player->LevelSolved_CountingTime = 0;
3285     player->LevelSolved_CountingScore = 0;
3286
3287     map_player_action[i] = i;
3288   }
3289
3290   network_player_action_received = FALSE;
3291
3292 #if defined(NETWORK_AVALIABLE)
3293   /* initial null action */
3294   if (network_playing)
3295     SendToServer_MovePlayer(MV_NONE);
3296 #endif
3297
3298   ZX = ZY = -1;
3299   ExitX = ExitY = -1;
3300
3301   FrameCounter = 0;
3302   TimeFrames = 0;
3303   TimePlayed = 0;
3304   TimeLeft = level.time;
3305   TapeTime = 0;
3306
3307   ScreenMovDir = MV_NONE;
3308   ScreenMovPos = 0;
3309   ScreenGfxPos = 0;
3310
3311   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3312
3313   AllPlayersGone = FALSE;
3314
3315   game.no_time_limit = (level.time == 0);
3316
3317   game.yamyam_content_nr = 0;
3318   game.robot_wheel_active = FALSE;
3319   game.magic_wall_active = FALSE;
3320   game.magic_wall_time_left = 0;
3321   game.light_time_left = 0;
3322   game.timegate_time_left = 0;
3323   game.switchgate_pos = 0;
3324   game.wind_direction = level.wind_direction_initial;
3325
3326   game.lenses_time_left = 0;
3327   game.magnify_time_left = 0;
3328
3329   game.ball_state = level.ball_state_initial;
3330   game.ball_content_nr = 0;
3331
3332   game.envelope_active = FALSE;
3333
3334   /* set focus to local player for network games, else to all players */
3335   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3336   game.centered_player_nr_next = game.centered_player_nr;
3337   game.set_centered_player = FALSE;
3338
3339   if (network_playing && tape.recording)
3340   {
3341     /* store client dependent player focus when recording network games */
3342     tape.centered_player_nr_next = game.centered_player_nr_next;
3343     tape.set_centered_player = TRUE;
3344   }
3345
3346   for (i = 0; i < NUM_BELTS; i++)
3347   {
3348     game.belt_dir[i] = MV_NONE;
3349     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3350   }
3351
3352   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3353     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3354
3355 #if DEBUG_INIT_PLAYER
3356   if (options.debug)
3357   {
3358     printf("Player status at level initialization:\n");
3359   }
3360 #endif
3361
3362   SCAN_PLAYFIELD(x, y)
3363   {
3364     Feld[x][y] = level.field[x][y];
3365     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3366     ChangeDelay[x][y] = 0;
3367     ChangePage[x][y] = -1;
3368     CustomValue[x][y] = 0;              /* initialized in InitField() */
3369     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3370     AmoebaNr[x][y] = 0;
3371     WasJustMoving[x][y] = 0;
3372     WasJustFalling[x][y] = 0;
3373     CheckCollision[x][y] = 0;
3374     CheckImpact[x][y] = 0;
3375     Stop[x][y] = FALSE;
3376     Pushed[x][y] = FALSE;
3377
3378     ChangeCount[x][y] = 0;
3379     ChangeEvent[x][y] = -1;
3380
3381     ExplodePhase[x][y] = 0;
3382     ExplodeDelay[x][y] = 0;
3383     ExplodeField[x][y] = EX_TYPE_NONE;
3384
3385     RunnerVisit[x][y] = 0;
3386     PlayerVisit[x][y] = 0;
3387
3388     GfxFrame[x][y] = 0;
3389     GfxRandom[x][y] = INIT_GFX_RANDOM();
3390     GfxElement[x][y] = EL_UNDEFINED;
3391     GfxAction[x][y] = ACTION_DEFAULT;
3392     GfxDir[x][y] = MV_NONE;
3393     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3394   }
3395
3396   SCAN_PLAYFIELD(x, y)
3397   {
3398     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3399       emulate_bd = FALSE;
3400     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3401       emulate_sb = FALSE;
3402     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3403       emulate_sp = FALSE;
3404
3405     InitField(x, y, TRUE);
3406
3407     ResetGfxAnimation(x, y);
3408   }
3409
3410   InitBeltMovement();
3411
3412   for (i = 0; i < MAX_PLAYERS; i++)
3413   {
3414     struct PlayerInfo *player = &stored_player[i];
3415
3416     /* set number of special actions for bored and sleeping animation */
3417     player->num_special_action_bored =
3418       get_num_special_action(player->artwork_element,
3419                              ACTION_BORING_1, ACTION_BORING_LAST);
3420     player->num_special_action_sleeping =
3421       get_num_special_action(player->artwork_element,
3422                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3423   }
3424
3425   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3426                     emulate_sb ? EMU_SOKOBAN :
3427                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3428
3429   /* initialize type of slippery elements */
3430   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3431   {
3432     if (!IS_CUSTOM_ELEMENT(i))
3433     {
3434       /* default: elements slip down either to the left or right randomly */
3435       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3436
3437       /* SP style elements prefer to slip down on the left side */
3438       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3439         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3440
3441       /* BD style elements prefer to slip down on the left side */
3442       if (game.emulation == EMU_BOULDERDASH)
3443         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3444     }
3445   }
3446
3447   /* initialize explosion and ignition delay */
3448   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449   {
3450     if (!IS_CUSTOM_ELEMENT(i))
3451     {
3452       int num_phase = 8;
3453       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3454                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3455                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3456       int last_phase = (num_phase + 1) * delay;
3457       int half_phase = (num_phase / 2) * delay;
3458
3459       element_info[i].explosion_delay = last_phase - 1;
3460       element_info[i].ignition_delay = half_phase;
3461
3462       if (i == EL_BLACK_ORB)
3463         element_info[i].ignition_delay = 1;
3464     }
3465   }
3466
3467   /* correct non-moving belts to start moving left */
3468   for (i = 0; i < NUM_BELTS; i++)
3469     if (game.belt_dir[i] == MV_NONE)
3470       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3471
3472 #if USE_NEW_PLAYER_ASSIGNMENTS
3473   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3474   /* choose default local player */
3475   local_player = &stored_player[0];
3476
3477   for (i = 0; i < MAX_PLAYERS; i++)
3478     stored_player[i].connected = FALSE;
3479
3480   local_player->connected = TRUE;
3481   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3482
3483   if (tape.playing)
3484   {
3485     for (i = 0; i < MAX_PLAYERS; i++)
3486       stored_player[i].connected = tape.player_participates[i];
3487   }
3488   else if (game.team_mode && !options.network)
3489   {
3490     /* try to guess locally connected team mode players (needed for correct
3491        assignment of player figures from level to locally playing players) */
3492
3493     for (i = 0; i < MAX_PLAYERS; i++)
3494       if (setup.input[i].use_joystick ||
3495           setup.input[i].key.left != KSYM_UNDEFINED)
3496         stored_player[i].connected = TRUE;
3497   }
3498
3499 #if DEBUG_INIT_PLAYER
3500   if (options.debug)
3501   {
3502     printf("Player status after level initialization:\n");
3503
3504     for (i = 0; i < MAX_PLAYERS; i++)
3505     {
3506       struct PlayerInfo *player = &stored_player[i];
3507
3508       printf("- player %d: present == %d, connected == %d, active == %d",
3509              i + 1,
3510              player->present,
3511              player->connected,
3512              player->active);
3513
3514       if (local_player == player)
3515         printf(" (local player)");
3516
3517       printf("\n");
3518     }
3519   }
3520 #endif
3521
3522 #if DEBUG_INIT_PLAYER
3523   if (options.debug)
3524     printf("Reassigning players ...\n");
3525 #endif
3526
3527   /* check if any connected player was not found in playfield */
3528   for (i = 0; i < MAX_PLAYERS; i++)
3529   {
3530     struct PlayerInfo *player = &stored_player[i];
3531
3532     if (player->connected && !player->present)
3533     {
3534       struct PlayerInfo *field_player = NULL;
3535
3536 #if DEBUG_INIT_PLAYER
3537       if (options.debug)
3538         printf("- looking for field player for player %d ...\n", i + 1);
3539 #endif
3540
3541       /* assign first free player found that is present in the playfield */
3542
3543       /* first try: look for unmapped playfield player that is not connected */
3544       for (j = 0; j < MAX_PLAYERS; j++)
3545         if (field_player == NULL &&
3546             stored_player[j].present &&
3547             !stored_player[j].mapped &&
3548             !stored_player[j].connected)
3549           field_player = &stored_player[j];
3550
3551       /* second try: look for *any* unmapped playfield player */
3552       for (j = 0; j < MAX_PLAYERS; j++)
3553         if (field_player == NULL &&
3554             stored_player[j].present &&
3555             !stored_player[j].mapped)
3556           field_player = &stored_player[j];
3557
3558       if (field_player != NULL)
3559       {
3560         int jx = field_player->jx, jy = field_player->jy;
3561
3562 #if DEBUG_INIT_PLAYER
3563         if (options.debug)
3564           printf("- found player %d\n", field_player->index_nr + 1);
3565 #endif
3566
3567         player->present = FALSE;
3568         player->active = FALSE;
3569
3570         field_player->present = TRUE;
3571         field_player->active = TRUE;
3572
3573         /*
3574         player->initial_element = field_player->initial_element;
3575         player->artwork_element = field_player->artwork_element;
3576
3577         player->block_last_field       = field_player->block_last_field;
3578         player->block_delay_adjustment = field_player->block_delay_adjustment;
3579         */
3580
3581         StorePlayer[jx][jy] = field_player->element_nr;
3582
3583         field_player->jx = field_player->last_jx = jx;
3584         field_player->jy = field_player->last_jy = jy;
3585
3586         if (local_player == player)
3587           local_player = field_player;
3588
3589         map_player_action[field_player->index_nr] = i;
3590
3591         field_player->mapped = TRUE;
3592
3593 #if DEBUG_INIT_PLAYER
3594         if (options.debug)
3595           printf("- map_player_action[%d] == %d\n",
3596                  field_player->index_nr + 1, i + 1);
3597 #endif
3598       }
3599     }
3600
3601     if (player->connected && player->present)
3602       player->mapped = TRUE;
3603   }
3604
3605 #if DEBUG_INIT_PLAYER
3606   if (options.debug)
3607   {
3608     printf("Player status after player assignment (first stage):\n");
3609
3610     for (i = 0; i < MAX_PLAYERS; i++)
3611     {
3612       struct PlayerInfo *player = &stored_player[i];
3613
3614       printf("- player %d: present == %d, connected == %d, active == %d",
3615              i + 1,
3616              player->present,
3617              player->connected,
3618              player->active);
3619
3620       if (local_player == player)
3621         printf(" (local player)");
3622
3623       printf("\n");
3624     }
3625   }
3626 #endif
3627
3628 #else
3629
3630   /* check if any connected player was not found in playfield */
3631   for (i = 0; i < MAX_PLAYERS; i++)
3632   {
3633     struct PlayerInfo *player = &stored_player[i];
3634
3635     if (player->connected && !player->present)
3636     {
3637       for (j = 0; j < MAX_PLAYERS; j++)
3638       {
3639         struct PlayerInfo *field_player = &stored_player[j];
3640         int jx = field_player->jx, jy = field_player->jy;
3641
3642         /* assign first free player found that is present in the playfield */
3643         if (field_player->present && !field_player->connected)
3644         {
3645           player->present = TRUE;
3646           player->active = TRUE;
3647
3648           field_player->present = FALSE;
3649           field_player->active = FALSE;
3650
3651           player->initial_element = field_player->initial_element;
3652           player->artwork_element = field_player->artwork_element;
3653
3654           player->block_last_field       = field_player->block_last_field;
3655           player->block_delay_adjustment = field_player->block_delay_adjustment;
3656
3657           StorePlayer[jx][jy] = player->element_nr;
3658
3659           player->jx = player->last_jx = jx;
3660           player->jy = player->last_jy = jy;
3661
3662           break;
3663         }
3664       }
3665     }
3666   }
3667 #endif
3668
3669 #if 0
3670   printf("::: local_player->present == %d\n", local_player->present);
3671 #endif
3672
3673   if (tape.playing)
3674   {
3675     /* when playing a tape, eliminate all players who do not participate */
3676
3677 #if USE_NEW_PLAYER_ASSIGNMENTS
3678
3679     if (!game.team_mode)
3680     {
3681       for (i = 0; i < MAX_PLAYERS; i++)
3682       {
3683         if (stored_player[i].active &&
3684             !tape.player_participates[map_player_action[i]])
3685         {
3686           struct PlayerInfo *player = &stored_player[i];
3687           int jx = player->jx, jy = player->jy;
3688
3689 #if DEBUG_INIT_PLAYER
3690           if (options.debug)
3691             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3692 #endif
3693
3694           player->active = FALSE;
3695           StorePlayer[jx][jy] = 0;
3696           Feld[jx][jy] = EL_EMPTY;
3697         }
3698       }
3699     }
3700
3701 #else
3702
3703     for (i = 0; i < MAX_PLAYERS; i++)
3704     {
3705       if (stored_player[i].active &&
3706           !tape.player_participates[i])
3707       {
3708         struct PlayerInfo *player = &stored_player[i];
3709         int jx = player->jx, jy = player->jy;
3710
3711         player->active = FALSE;
3712         StorePlayer[jx][jy] = 0;
3713         Feld[jx][jy] = EL_EMPTY;
3714       }
3715     }
3716 #endif
3717   }
3718   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3719   {
3720     /* when in single player mode, eliminate all but the first active player */
3721
3722     for (i = 0; i < MAX_PLAYERS; i++)
3723     {
3724       if (stored_player[i].active)
3725       {
3726         for (j = i + 1; j < MAX_PLAYERS; j++)
3727         {
3728           if (stored_player[j].active)
3729           {
3730             struct PlayerInfo *player = &stored_player[j];
3731             int jx = player->jx, jy = player->jy;
3732
3733             player->active = FALSE;
3734             player->present = FALSE;
3735
3736             StorePlayer[jx][jy] = 0;
3737             Feld[jx][jy] = EL_EMPTY;
3738           }
3739         }
3740       }
3741     }
3742   }
3743
3744   /* when recording the game, store which players take part in the game */
3745   if (tape.recording)
3746   {
3747 #if USE_NEW_PLAYER_ASSIGNMENTS
3748     for (i = 0; i < MAX_PLAYERS; i++)
3749       if (stored_player[i].connected)
3750         tape.player_participates[i] = TRUE;
3751 #else
3752     for (i = 0; i < MAX_PLAYERS; i++)
3753       if (stored_player[i].active)
3754         tape.player_participates[i] = TRUE;
3755 #endif
3756   }
3757
3758 #if DEBUG_INIT_PLAYER
3759   if (options.debug)
3760   {
3761     printf("Player status after player assignment (final stage):\n");
3762
3763     for (i = 0; i < MAX_PLAYERS; i++)
3764     {
3765       struct PlayerInfo *player = &stored_player[i];
3766
3767       printf("- player %d: present == %d, connected == %d, active == %d",
3768              i + 1,
3769              player->present,
3770              player->connected,
3771              player->active);
3772
3773       if (local_player == player)
3774         printf(" (local player)");
3775
3776       printf("\n");
3777     }
3778   }
3779 #endif
3780
3781   if (BorderElement == EL_EMPTY)
3782   {
3783     SBX_Left = 0;
3784     SBX_Right = lev_fieldx - SCR_FIELDX;
3785     SBY_Upper = 0;
3786     SBY_Lower = lev_fieldy - SCR_FIELDY;
3787   }
3788   else
3789   {
3790     SBX_Left = -1;
3791     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3792     SBY_Upper = -1;
3793     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3794   }
3795
3796   if (full_lev_fieldx <= SCR_FIELDX)
3797     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3798   if (full_lev_fieldy <= SCR_FIELDY)
3799     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3800
3801   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3802     SBX_Left--;
3803   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3804     SBY_Upper--;
3805
3806   /* if local player not found, look for custom element that might create
3807      the player (make some assumptions about the right custom element) */
3808   if (!local_player->present)
3809   {
3810     int start_x = 0, start_y = 0;
3811     int found_rating = 0;
3812     int found_element = EL_UNDEFINED;
3813     int player_nr = local_player->index_nr;
3814
3815     SCAN_PLAYFIELD(x, y)
3816     {
3817       int element = Feld[x][y];
3818       int content;
3819       int xx, yy;
3820       boolean is_player;
3821
3822       if (level.use_start_element[player_nr] &&
3823           level.start_element[player_nr] == element &&
3824           found_rating < 4)
3825       {
3826         start_x = x;
3827         start_y = y;
3828
3829         found_rating = 4;
3830         found_element = element;
3831       }
3832
3833       if (!IS_CUSTOM_ELEMENT(element))
3834         continue;
3835
3836       if (CAN_CHANGE(element))
3837       {
3838         for (i = 0; i < element_info[element].num_change_pages; i++)
3839         {
3840           /* check for player created from custom element as single target */
3841           content = element_info[element].change_page[i].target_element;
3842           is_player = ELEM_IS_PLAYER(content);
3843
3844           if (is_player && (found_rating < 3 ||
3845                             (found_rating == 3 && element < found_element)))
3846           {
3847             start_x = x;
3848             start_y = y;
3849
3850             found_rating = 3;
3851             found_element = element;
3852           }
3853         }
3854       }
3855
3856       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3857       {
3858         /* check for player created from custom element as explosion content */
3859         content = element_info[element].content.e[xx][yy];
3860         is_player = ELEM_IS_PLAYER(content);
3861
3862         if (is_player && (found_rating < 2 ||
3863                           (found_rating == 2 && element < found_element)))
3864         {
3865           start_x = x + xx - 1;
3866           start_y = y + yy - 1;
3867
3868           found_rating = 2;
3869           found_element = element;
3870         }
3871
3872         if (!CAN_CHANGE(element))
3873           continue;
3874
3875         for (i = 0; i < element_info[element].num_change_pages; i++)
3876         {
3877           /* check for player created from custom element as extended target */
3878           content =
3879             element_info[element].change_page[i].target_content.e[xx][yy];
3880
3881           is_player = ELEM_IS_PLAYER(content);
3882
3883           if (is_player && (found_rating < 1 ||
3884                             (found_rating == 1 && element < found_element)))
3885           {
3886             start_x = x + xx - 1;
3887             start_y = y + yy - 1;
3888
3889             found_rating = 1;
3890             found_element = element;
3891           }
3892         }
3893       }
3894     }
3895
3896     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3897                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3898                 start_x - MIDPOSX);
3899
3900     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3901                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3902                 start_y - MIDPOSY);
3903   }
3904   else
3905   {
3906     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3907                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3908                 local_player->jx - MIDPOSX);
3909
3910     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3911                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3912                 local_player->jy - MIDPOSY);
3913   }
3914
3915   /* !!! FIX THIS (START) !!! */
3916   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3917   {
3918     InitGameEngine_EM();
3919   }
3920   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3921   {
3922     InitGameEngine_SP();
3923   }
3924   else
3925   {
3926     DrawLevel(REDRAW_FIELD);
3927     DrawAllPlayers();
3928
3929     /* after drawing the level, correct some elements */
3930     if (game.timegate_time_left == 0)
3931       CloseAllOpenTimegates();
3932   }
3933
3934   /* blit playfield from scroll buffer to normal back buffer for fading in */
3935   BlitScreenToBitmap(backbuffer);
3936   /* !!! FIX THIS (END) !!! */
3937
3938   DrawMaskedBorder(fade_mask);
3939
3940   FadeIn(fade_mask);
3941
3942 #if 1
3943   // full screen redraw is required at this point in the following cases:
3944   // - special editor door undrawn when game was started from level editor
3945   // - drawing area (playfield) was changed and has to be removed completely
3946   redraw_mask = REDRAW_ALL;
3947   BackToFront();
3948 #endif
3949
3950   if (!game.restart_level)
3951   {
3952     /* copy default game door content to main double buffer */
3953
3954     /* !!! CHECK AGAIN !!! */
3955     SetPanelBackground();
3956     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3957     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3958   }
3959
3960   SetPanelBackground();
3961   SetDrawBackgroundMask(REDRAW_DOOR_1);
3962
3963   UpdateAndDisplayGameControlValues();
3964
3965   if (!game.restart_level)
3966   {
3967     UnmapGameButtons();
3968     UnmapTapeButtons();
3969
3970     FreeGameButtons();
3971     CreateGameButtons();
3972
3973     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3974     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3975     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3976
3977     MapGameButtons();
3978     MapTapeButtons();
3979
3980     /* copy actual game door content to door double buffer for OpenDoor() */
3981     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3982
3983     OpenDoor(DOOR_OPEN_ALL);
3984
3985     PlaySound(SND_GAME_STARTING);
3986
3987     if (setup.sound_music)
3988       PlayLevelMusic();
3989
3990     KeyboardAutoRepeatOffUnlessAutoplay();
3991
3992 #if DEBUG_INIT_PLAYER
3993     if (options.debug)
3994     {
3995       printf("Player status (final):\n");
3996
3997       for (i = 0; i < MAX_PLAYERS; i++)
3998       {
3999         struct PlayerInfo *player = &stored_player[i];
4000
4001         printf("- player %d: present == %d, connected == %d, active == %d",
4002                i + 1,
4003                player->present,
4004                player->connected,
4005                player->active);
4006
4007         if (local_player == player)
4008           printf(" (local player)");
4009
4010         printf("\n");
4011       }
4012     }
4013 #endif
4014   }
4015
4016   UnmapAllGadgets();
4017
4018   MapGameButtons();
4019   MapTapeButtons();
4020
4021   if (!game.restart_level && !tape.playing)
4022   {
4023     LevelStats_incPlayed(level_nr);
4024
4025     SaveLevelSetup_SeriesInfo();
4026   }
4027
4028   game.restart_level = FALSE;
4029
4030   SaveEngineSnapshotToListInitial();
4031 }
4032
4033 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4034 {
4035   /* this is used for non-R'n'D game engines to update certain engine values */
4036
4037   /* needed to determine if sounds are played within the visible screen area */
4038   scroll_x = actual_scroll_x;
4039   scroll_y = actual_scroll_y;
4040 }
4041
4042 void InitMovDir(int x, int y)
4043 {
4044   int i, element = Feld[x][y];
4045   static int xy[4][2] =
4046   {
4047     {  0, +1 },
4048     { +1,  0 },
4049     {  0, -1 },
4050     { -1,  0 }
4051   };
4052   static int direction[3][4] =
4053   {
4054     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4055     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4056     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4057   };
4058
4059   switch (element)
4060   {
4061     case EL_BUG_RIGHT:
4062     case EL_BUG_UP:
4063     case EL_BUG_LEFT:
4064     case EL_BUG_DOWN:
4065       Feld[x][y] = EL_BUG;
4066       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4067       break;
4068
4069     case EL_SPACESHIP_RIGHT:
4070     case EL_SPACESHIP_UP:
4071     case EL_SPACESHIP_LEFT:
4072     case EL_SPACESHIP_DOWN:
4073       Feld[x][y] = EL_SPACESHIP;
4074       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4075       break;
4076
4077     case EL_BD_BUTTERFLY_RIGHT:
4078     case EL_BD_BUTTERFLY_UP:
4079     case EL_BD_BUTTERFLY_LEFT:
4080     case EL_BD_BUTTERFLY_DOWN:
4081       Feld[x][y] = EL_BD_BUTTERFLY;
4082       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4083       break;
4084
4085     case EL_BD_FIREFLY_RIGHT:
4086     case EL_BD_FIREFLY_UP:
4087     case EL_BD_FIREFLY_LEFT:
4088     case EL_BD_FIREFLY_DOWN:
4089       Feld[x][y] = EL_BD_FIREFLY;
4090       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4091       break;
4092
4093     case EL_PACMAN_RIGHT:
4094     case EL_PACMAN_UP:
4095     case EL_PACMAN_LEFT:
4096     case EL_PACMAN_DOWN:
4097       Feld[x][y] = EL_PACMAN;
4098       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4099       break;
4100
4101     case EL_YAMYAM_LEFT:
4102     case EL_YAMYAM_RIGHT:
4103     case EL_YAMYAM_UP:
4104     case EL_YAMYAM_DOWN:
4105       Feld[x][y] = EL_YAMYAM;
4106       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4107       break;
4108
4109     case EL_SP_SNIKSNAK:
4110       MovDir[x][y] = MV_UP;
4111       break;
4112
4113     case EL_SP_ELECTRON:
4114       MovDir[x][y] = MV_LEFT;
4115       break;
4116
4117     case EL_MOLE_LEFT:
4118     case EL_MOLE_RIGHT:
4119     case EL_MOLE_UP:
4120     case EL_MOLE_DOWN:
4121       Feld[x][y] = EL_MOLE;
4122       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4123       break;
4124
4125     default:
4126       if (IS_CUSTOM_ELEMENT(element))
4127       {
4128         struct ElementInfo *ei = &element_info[element];
4129         int move_direction_initial = ei->move_direction_initial;
4130         int move_pattern = ei->move_pattern;
4131
4132         if (move_direction_initial == MV_START_PREVIOUS)
4133         {
4134           if (MovDir[x][y] != MV_NONE)
4135             return;
4136
4137           move_direction_initial = MV_START_AUTOMATIC;
4138         }
4139
4140         if (move_direction_initial == MV_START_RANDOM)
4141           MovDir[x][y] = 1 << RND(4);
4142         else if (move_direction_initial & MV_ANY_DIRECTION)
4143           MovDir[x][y] = move_direction_initial;
4144         else if (move_pattern == MV_ALL_DIRECTIONS ||
4145                  move_pattern == MV_TURNING_LEFT ||
4146                  move_pattern == MV_TURNING_RIGHT ||
4147                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4148                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4149                  move_pattern == MV_TURNING_RANDOM)
4150           MovDir[x][y] = 1 << RND(4);
4151         else if (move_pattern == MV_HORIZONTAL)
4152           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4153         else if (move_pattern == MV_VERTICAL)
4154           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4155         else if (move_pattern & MV_ANY_DIRECTION)
4156           MovDir[x][y] = element_info[element].move_pattern;
4157         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4158                  move_pattern == MV_ALONG_RIGHT_SIDE)
4159         {
4160           /* use random direction as default start direction */
4161           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4162             MovDir[x][y] = 1 << RND(4);
4163
4164           for (i = 0; i < NUM_DIRECTIONS; i++)
4165           {
4166             int x1 = x + xy[i][0];
4167             int y1 = y + xy[i][1];
4168
4169             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4170             {
4171               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4172                 MovDir[x][y] = direction[0][i];
4173               else
4174                 MovDir[x][y] = direction[1][i];
4175
4176               break;
4177             }
4178           }
4179         }                
4180       }
4181       else
4182       {
4183         MovDir[x][y] = 1 << RND(4);
4184
4185         if (element != EL_BUG &&
4186             element != EL_SPACESHIP &&
4187             element != EL_BD_BUTTERFLY &&
4188             element != EL_BD_FIREFLY)
4189           break;
4190
4191         for (i = 0; i < NUM_DIRECTIONS; i++)
4192         {
4193           int x1 = x + xy[i][0];
4194           int y1 = y + xy[i][1];
4195
4196           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4197           {
4198             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4199             {
4200               MovDir[x][y] = direction[0][i];
4201               break;
4202             }
4203             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4204                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4205             {
4206               MovDir[x][y] = direction[1][i];
4207               break;
4208             }
4209           }
4210         }
4211       }
4212       break;
4213   }
4214
4215   GfxDir[x][y] = MovDir[x][y];
4216 }
4217
4218 void InitAmoebaNr(int x, int y)
4219 {
4220   int i;
4221   int group_nr = AmoebeNachbarNr(x, y);
4222
4223   if (group_nr == 0)
4224   {
4225     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4226     {
4227       if (AmoebaCnt[i] == 0)
4228       {
4229         group_nr = i;
4230         break;
4231       }
4232     }
4233   }
4234
4235   AmoebaNr[x][y] = group_nr;
4236   AmoebaCnt[group_nr]++;
4237   AmoebaCnt2[group_nr]++;
4238 }
4239
4240 static void PlayerWins(struct PlayerInfo *player)
4241 {
4242   player->LevelSolved = TRUE;
4243   player->GameOver = TRUE;
4244
4245   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4246                          level.native_em_level->lev->score : player->score);
4247
4248   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4249                                       TimeLeft);
4250   player->LevelSolved_CountingScore = player->score_final;
4251 }
4252
4253 void GameWon()
4254 {
4255   static int time, time_final;
4256   static int score, score_final;
4257   static int game_over_delay_1 = 0;
4258   static int game_over_delay_2 = 0;
4259   int game_over_delay_value_1 = 50;
4260   int game_over_delay_value_2 = 50;
4261
4262   if (!local_player->LevelSolved_GameWon)
4263   {
4264     int i;
4265
4266     /* do not start end game actions before the player stops moving (to exit) */
4267     if (local_player->MovPos)
4268       return;
4269
4270     local_player->LevelSolved_GameWon = TRUE;
4271     local_player->LevelSolved_SaveTape = tape.recording;
4272     local_player->LevelSolved_SaveScore = !tape.playing;
4273
4274     if (!tape.playing)
4275     {
4276       LevelStats_incSolved(level_nr);
4277
4278       SaveLevelSetup_SeriesInfo();
4279     }
4280
4281     if (tape.auto_play)         /* tape might already be stopped here */
4282       tape.auto_play_level_solved = TRUE;
4283
4284     TapeStop();
4285
4286     game_over_delay_1 = game_over_delay_value_1;
4287     game_over_delay_2 = game_over_delay_value_2;
4288
4289     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4290     score = score_final = local_player->score_final;
4291
4292     if (TimeLeft > 0)
4293     {
4294       time_final = 0;
4295       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4296     }
4297     else if (game.no_time_limit && TimePlayed < 999)
4298     {
4299       time_final = 999;
4300       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4301     }
4302
4303     local_player->score_final = score_final;
4304
4305     if (level_editor_test_game)
4306     {
4307       time = time_final;
4308       score = score_final;
4309
4310       local_player->LevelSolved_CountingTime = time;
4311       local_player->LevelSolved_CountingScore = score;
4312
4313       game_panel_controls[GAME_PANEL_TIME].value = time;
4314       game_panel_controls[GAME_PANEL_SCORE].value = score;
4315
4316       DisplayGameControlValues();
4317     }
4318
4319     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4320     {
4321       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4322       {
4323         /* close exit door after last player */
4324         if ((AllPlayersGone &&
4325              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4326               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4327               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4328             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4329             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4330         {
4331           int element = Feld[ExitX][ExitY];
4332
4333           Feld[ExitX][ExitY] =
4334             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4335              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4336              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4337              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4338              EL_EM_STEEL_EXIT_CLOSING);
4339
4340           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4341         }
4342
4343         /* player disappears */
4344         DrawLevelField(ExitX, ExitY);
4345       }
4346
4347       for (i = 0; i < MAX_PLAYERS; i++)
4348       {
4349         struct PlayerInfo *player = &stored_player[i];
4350
4351         if (player->present)
4352         {
4353           RemovePlayer(player);
4354
4355           /* player disappears */
4356           DrawLevelField(player->jx, player->jy);
4357         }
4358       }
4359     }
4360
4361     PlaySound(SND_GAME_WINNING);
4362   }
4363
4364   if (game_over_delay_1 > 0)
4365   {
4366     game_over_delay_1--;
4367
4368     return;
4369   }
4370
4371   if (time != time_final)
4372   {
4373     int time_to_go = ABS(time_final - time);
4374     int time_count_dir = (time < time_final ? +1 : -1);
4375     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4376
4377     time  += time_count_steps * time_count_dir;
4378     score += time_count_steps * level.score[SC_TIME_BONUS];
4379
4380     local_player->LevelSolved_CountingTime = time;
4381     local_player->LevelSolved_CountingScore = score;
4382
4383     game_panel_controls[GAME_PANEL_TIME].value = time;
4384     game_panel_controls[GAME_PANEL_SCORE].value = score;
4385
4386     DisplayGameControlValues();
4387
4388     if (time == time_final)
4389       StopSound(SND_GAME_LEVELTIME_BONUS);
4390     else if (setup.sound_loops)
4391       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4392     else
4393       PlaySound(SND_GAME_LEVELTIME_BONUS);
4394
4395     return;
4396   }
4397
4398   local_player->LevelSolved_PanelOff = TRUE;
4399
4400   if (game_over_delay_2 > 0)
4401   {
4402     game_over_delay_2--;
4403
4404     return;
4405   }
4406
4407   GameEnd();
4408 }
4409
4410 void GameEnd()
4411 {
4412   int hi_pos;
4413   boolean raise_level = FALSE;
4414
4415   local_player->LevelSolved_GameEnd = TRUE;
4416
4417   if (!global.use_envelope_request)
4418     CloseDoor(DOOR_CLOSE_1);
4419
4420   if (local_player->LevelSolved_SaveTape)
4421   {
4422     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4423   }
4424
4425   CloseDoor(DOOR_CLOSE_ALL);
4426
4427   if (level_editor_test_game)
4428   {
4429     SetGameStatus(GAME_MODE_MAIN);
4430
4431     DrawMainMenu();
4432
4433     return;
4434   }
4435
4436   if (!local_player->LevelSolved_SaveScore)
4437   {
4438     SetGameStatus(GAME_MODE_MAIN);
4439
4440     DrawMainMenu();
4441
4442     return;
4443   }
4444
4445   if (level_nr == leveldir_current->handicap_level)
4446   {
4447     leveldir_current->handicap_level++;
4448
4449     SaveLevelSetup_SeriesInfo();
4450   }
4451
4452   if (level_nr < leveldir_current->last_level)
4453     raise_level = TRUE;                 /* advance to next level */
4454
4455   if ((hi_pos = NewHiScore()) >= 0) 
4456   {
4457     SetGameStatus(GAME_MODE_SCORES);
4458
4459     DrawHallOfFame(hi_pos);
4460
4461     if (raise_level)
4462     {
4463       level_nr++;
4464       TapeErase();
4465     }
4466   }
4467   else
4468   {
4469     SetGameStatus(GAME_MODE_MAIN);
4470
4471     if (raise_level)
4472     {
4473       level_nr++;
4474       TapeErase();
4475     }
4476
4477     DrawMainMenu();
4478   }
4479 }
4480
4481 int NewHiScore()
4482 {
4483   int k, l;
4484   int position = -1;
4485
4486   LoadScore(level_nr);
4487
4488   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4489       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4490     return -1;
4491
4492   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4493   {
4494     if (local_player->score_final > highscore[k].Score)
4495     {
4496       /* player has made it to the hall of fame */
4497
4498       if (k < MAX_SCORE_ENTRIES - 1)
4499       {
4500         int m = MAX_SCORE_ENTRIES - 1;
4501
4502 #ifdef ONE_PER_NAME
4503         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4504           if (strEqual(setup.player_name, highscore[l].Name))
4505             m = l;
4506         if (m == k)     /* player's new highscore overwrites his old one */
4507           goto put_into_list;
4508 #endif
4509
4510         for (l = m; l > k; l--)
4511         {
4512           strcpy(highscore[l].Name, highscore[l - 1].Name);
4513           highscore[l].Score = highscore[l - 1].Score;
4514         }
4515       }
4516
4517 #ifdef ONE_PER_NAME
4518       put_into_list:
4519 #endif
4520       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4521       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4522       highscore[k].Score = local_player->score_final; 
4523       position = k;
4524       break;
4525     }
4526
4527 #ifdef ONE_PER_NAME
4528     else if (!strncmp(setup.player_name, highscore[k].Name,
4529                       MAX_PLAYER_NAME_LEN))
4530       break;    /* player already there with a higher score */
4531 #endif
4532
4533   }
4534
4535   if (position >= 0) 
4536     SaveScore(level_nr);
4537
4538   return position;
4539 }
4540
4541 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4542 {
4543   int element = Feld[x][y];
4544   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4545   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4546   int horiz_move = (dx != 0);
4547   int sign = (horiz_move ? dx : dy);
4548   int step = sign * element_info[element].move_stepsize;
4549
4550   /* special values for move stepsize for spring and things on conveyor belt */
4551   if (horiz_move)
4552   {
4553     if (CAN_FALL(element) &&
4554         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4555       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4556     else if (element == EL_SPRING)
4557       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4558   }
4559
4560   return step;
4561 }
4562
4563 inline static int getElementMoveStepsize(int x, int y)
4564 {
4565   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4566 }
4567
4568 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4569 {
4570   if (player->GfxAction != action || player->GfxDir != dir)
4571   {
4572     player->GfxAction = action;
4573     player->GfxDir = dir;
4574     player->Frame = 0;
4575     player->StepFrame = 0;
4576   }
4577 }
4578
4579 static void ResetGfxFrame(int x, int y, boolean redraw)
4580 {
4581   int element = Feld[x][y];
4582   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4583   int last_gfx_frame = GfxFrame[x][y];
4584
4585   if (graphic_info[graphic].anim_global_sync)
4586     GfxFrame[x][y] = FrameCounter;
4587   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4588     GfxFrame[x][y] = CustomValue[x][y];
4589   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4590     GfxFrame[x][y] = element_info[element].collect_score;
4591   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4592     GfxFrame[x][y] = ChangeDelay[x][y];
4593
4594   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4595     DrawLevelGraphicAnimation(x, y, graphic);
4596 }
4597
4598 static void ResetGfxAnimation(int x, int y)
4599 {
4600   GfxAction[x][y] = ACTION_DEFAULT;
4601   GfxDir[x][y] = MovDir[x][y];
4602   GfxFrame[x][y] = 0;
4603
4604   ResetGfxFrame(x, y, FALSE);
4605 }
4606
4607 static void ResetRandomAnimationValue(int x, int y)
4608 {
4609   GfxRandom[x][y] = INIT_GFX_RANDOM();
4610 }
4611
4612 void InitMovingField(int x, int y, int direction)
4613 {
4614   int element = Feld[x][y];
4615   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4616   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4617   int newx = x + dx;
4618   int newy = y + dy;
4619   boolean is_moving_before, is_moving_after;
4620
4621   /* check if element was/is moving or being moved before/after mode change */
4622   is_moving_before = (WasJustMoving[x][y] != 0);
4623   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4624
4625   /* reset animation only for moving elements which change direction of moving
4626      or which just started or stopped moving
4627      (else CEs with property "can move" / "not moving" are reset each frame) */
4628   if (is_moving_before != is_moving_after ||
4629       direction != MovDir[x][y])
4630     ResetGfxAnimation(x, y);
4631
4632   MovDir[x][y] = direction;
4633   GfxDir[x][y] = direction;
4634
4635   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4636                      direction == MV_DOWN && CAN_FALL(element) ?
4637                      ACTION_FALLING : ACTION_MOVING);
4638
4639   /* this is needed for CEs with property "can move" / "not moving" */
4640
4641   if (is_moving_after)
4642   {
4643     if (Feld[newx][newy] == EL_EMPTY)
4644       Feld[newx][newy] = EL_BLOCKED;
4645
4646     MovDir[newx][newy] = MovDir[x][y];
4647
4648     CustomValue[newx][newy] = CustomValue[x][y];
4649
4650     GfxFrame[newx][newy] = GfxFrame[x][y];
4651     GfxRandom[newx][newy] = GfxRandom[x][y];
4652     GfxAction[newx][newy] = GfxAction[x][y];
4653     GfxDir[newx][newy] = GfxDir[x][y];
4654   }
4655 }
4656
4657 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4658 {
4659   int direction = MovDir[x][y];
4660   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4661   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4662
4663   *goes_to_x = newx;
4664   *goes_to_y = newy;
4665 }
4666
4667 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4668 {
4669   int oldx = x, oldy = y;
4670   int direction = MovDir[x][y];
4671
4672   if (direction == MV_LEFT)
4673     oldx++;
4674   else if (direction == MV_RIGHT)
4675     oldx--;
4676   else if (direction == MV_UP)
4677     oldy++;
4678   else if (direction == MV_DOWN)
4679     oldy--;
4680
4681   *comes_from_x = oldx;
4682   *comes_from_y = oldy;
4683 }
4684
4685 int MovingOrBlocked2Element(int x, int y)
4686 {
4687   int element = Feld[x][y];
4688
4689   if (element == EL_BLOCKED)
4690   {
4691     int oldx, oldy;
4692
4693     Blocked2Moving(x, y, &oldx, &oldy);
4694     return Feld[oldx][oldy];
4695   }
4696   else
4697     return element;
4698 }
4699
4700 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4701 {
4702   /* like MovingOrBlocked2Element(), but if element is moving
4703      and (x,y) is the field the moving element is just leaving,
4704      return EL_BLOCKED instead of the element value */
4705   int element = Feld[x][y];
4706
4707   if (IS_MOVING(x, y))
4708   {
4709     if (element == EL_BLOCKED)
4710     {
4711       int oldx, oldy;
4712
4713       Blocked2Moving(x, y, &oldx, &oldy);
4714       return Feld[oldx][oldy];
4715     }
4716     else
4717       return EL_BLOCKED;
4718   }
4719   else
4720     return element;
4721 }
4722
4723 static void RemoveField(int x, int y)
4724 {
4725   Feld[x][y] = EL_EMPTY;
4726
4727   MovPos[x][y] = 0;
4728   MovDir[x][y] = 0;
4729   MovDelay[x][y] = 0;
4730
4731   CustomValue[x][y] = 0;
4732
4733   AmoebaNr[x][y] = 0;
4734   ChangeDelay[x][y] = 0;
4735   ChangePage[x][y] = -1;
4736   Pushed[x][y] = FALSE;
4737
4738   GfxElement[x][y] = EL_UNDEFINED;
4739   GfxAction[x][y] = ACTION_DEFAULT;
4740   GfxDir[x][y] = MV_NONE;
4741 }
4742
4743 void RemoveMovingField(int x, int y)
4744 {
4745   int oldx = x, oldy = y, newx = x, newy = y;
4746   int element = Feld[x][y];
4747   int next_element = EL_UNDEFINED;
4748
4749   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4750     return;
4751
4752   if (IS_MOVING(x, y))
4753   {
4754     Moving2Blocked(x, y, &newx, &newy);
4755
4756     if (Feld[newx][newy] != EL_BLOCKED)
4757     {
4758       /* element is moving, but target field is not free (blocked), but
4759          already occupied by something different (example: acid pool);
4760          in this case, only remove the moving field, but not the target */
4761
4762       RemoveField(oldx, oldy);
4763
4764       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4765
4766       TEST_DrawLevelField(oldx, oldy);
4767
4768       return;
4769     }
4770   }
4771   else if (element == EL_BLOCKED)
4772   {
4773     Blocked2Moving(x, y, &oldx, &oldy);
4774     if (!IS_MOVING(oldx, oldy))
4775       return;
4776   }
4777
4778   if (element == EL_BLOCKED &&
4779       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4780        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4781        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4782        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4783        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4784        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4785     next_element = get_next_element(Feld[oldx][oldy]);
4786
4787   RemoveField(oldx, oldy);
4788   RemoveField(newx, newy);
4789
4790   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4791
4792   if (next_element != EL_UNDEFINED)
4793     Feld[oldx][oldy] = next_element;
4794
4795   TEST_DrawLevelField(oldx, oldy);
4796   TEST_DrawLevelField(newx, newy);
4797 }
4798
4799 void DrawDynamite(int x, int y)
4800 {
4801   int sx = SCREENX(x), sy = SCREENY(y);
4802   int graphic = el2img(Feld[x][y]);
4803   int frame;
4804
4805   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4806     return;
4807
4808   if (IS_WALKABLE_INSIDE(Back[x][y]))
4809     return;
4810
4811   if (Back[x][y])
4812     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4813   else if (Store[x][y])
4814     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4815
4816   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4817
4818   if (Back[x][y] || Store[x][y])
4819     DrawGraphicThruMask(sx, sy, graphic, frame);
4820   else
4821     DrawGraphic(sx, sy, graphic, frame);
4822 }
4823
4824 void CheckDynamite(int x, int y)
4825 {
4826   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4827   {
4828     MovDelay[x][y]--;
4829
4830     if (MovDelay[x][y] != 0)
4831     {
4832       DrawDynamite(x, y);
4833       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4834
4835       return;
4836     }
4837   }
4838
4839   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4840
4841   Bang(x, y);
4842 }
4843
4844 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4845 {
4846   boolean num_checked_players = 0;
4847   int i;
4848
4849   for (i = 0; i < MAX_PLAYERS; i++)
4850   {
4851     if (stored_player[i].active)
4852     {
4853       int sx = stored_player[i].jx;
4854       int sy = stored_player[i].jy;
4855
4856       if (num_checked_players == 0)
4857       {
4858         *sx1 = *sx2 = sx;
4859         *sy1 = *sy2 = sy;
4860       }
4861       else
4862       {
4863         *sx1 = MIN(*sx1, sx);
4864         *sy1 = MIN(*sy1, sy);
4865         *sx2 = MAX(*sx2, sx);
4866         *sy2 = MAX(*sy2, sy);
4867       }
4868
4869       num_checked_players++;
4870     }
4871   }
4872 }
4873
4874 static boolean checkIfAllPlayersFitToScreen_RND()
4875 {
4876   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4877
4878   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4879
4880   return (sx2 - sx1 < SCR_FIELDX &&
4881           sy2 - sy1 < SCR_FIELDY);
4882 }
4883
4884 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4885 {
4886   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4887
4888   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4889
4890   *sx = (sx1 + sx2) / 2;
4891   *sy = (sy1 + sy2) / 2;
4892 }
4893
4894 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4895                         boolean center_screen, boolean quick_relocation)
4896 {
4897   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4898   boolean no_delay = (tape.warp_forward);
4899   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4900   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4901   int new_scroll_x, new_scroll_y;
4902
4903   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4904   {
4905     /* case 1: quick relocation inside visible screen (without scrolling) */
4906
4907     RedrawPlayfield();
4908
4909     return;
4910   }
4911
4912   if (!level.shifted_relocation || center_screen)
4913   {
4914     /* relocation _with_ centering of screen */
4915
4916     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4917                     x > SBX_Right + MIDPOSX ? SBX_Right :
4918                     x - MIDPOSX);
4919
4920     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4921                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4922                     y - MIDPOSY);
4923   }
4924   else
4925   {
4926     /* relocation _without_ centering of screen */
4927
4928     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4929                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4930                            old_x - MIDPOSX);
4931
4932     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4933                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4934                            old_y - MIDPOSY);
4935
4936     int offset_x = x + (scroll_x - center_scroll_x);
4937     int offset_y = y + (scroll_y - center_scroll_y);
4938
4939     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4940                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4941                     offset_x - MIDPOSX);
4942
4943     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4944                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4945                     offset_y - MIDPOSY);
4946   }
4947
4948   if (quick_relocation)
4949   {
4950     /* case 2: quick relocation (redraw without visible scrolling) */
4951
4952     scroll_x = new_scroll_x;
4953     scroll_y = new_scroll_y;
4954
4955     RedrawPlayfield();
4956
4957     return;
4958   }
4959
4960   /* case 3: visible relocation (with scrolling to new position) */
4961
4962   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4963
4964   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4965   {
4966     int dx = 0, dy = 0;
4967     int fx = FX, fy = FY;
4968
4969     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4970     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4971
4972     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4973       break;
4974
4975     scroll_x -= dx;
4976     scroll_y -= dy;
4977
4978     fx += dx * TILEX / 2;
4979     fy += dy * TILEY / 2;
4980
4981     ScrollLevel(dx, dy);
4982     DrawAllPlayers();
4983
4984     /* scroll in two steps of half tile size to make things smoother */
4985     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4986     Delay(wait_delay_value);
4987
4988     /* scroll second step to align at full tile size */
4989     BackToFront();
4990     Delay(wait_delay_value);
4991   }
4992
4993   DrawAllPlayers();
4994   BackToFront();
4995   Delay(wait_delay_value);
4996 }
4997
4998 void RelocatePlayer(int jx, int jy, int el_player_raw)
4999 {
5000   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5001   int player_nr = GET_PLAYER_NR(el_player);
5002   struct PlayerInfo *player = &stored_player[player_nr];
5003   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5004   boolean no_delay = (tape.warp_forward);
5005   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5006   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5007   int old_jx = player->jx;
5008   int old_jy = player->jy;
5009   int old_element = Feld[old_jx][old_jy];
5010   int element = Feld[jx][jy];
5011   boolean player_relocated = (old_jx != jx || old_jy != jy);
5012
5013   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5014   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5015   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5016   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5017   int leave_side_horiz = move_dir_horiz;
5018   int leave_side_vert  = move_dir_vert;
5019   int enter_side = enter_side_horiz | enter_side_vert;
5020   int leave_side = leave_side_horiz | leave_side_vert;
5021
5022   if (player->GameOver)         /* do not reanimate dead player */
5023     return;
5024
5025   if (!player_relocated)        /* no need to relocate the player */
5026     return;
5027
5028   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5029   {
5030     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5031     DrawLevelField(jx, jy);
5032   }
5033
5034   if (player->present)
5035   {
5036     while (player->MovPos)
5037     {
5038       ScrollPlayer(player, SCROLL_GO_ON);
5039       ScrollScreen(NULL, SCROLL_GO_ON);
5040
5041       AdvanceFrameAndPlayerCounters(player->index_nr);
5042
5043       DrawPlayer(player);
5044
5045       BackToFront();
5046       Delay(wait_delay_value);
5047     }
5048
5049     DrawPlayer(player);         /* needed here only to cleanup last field */
5050     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5051
5052     player->is_moving = FALSE;
5053   }
5054
5055   if (IS_CUSTOM_ELEMENT(old_element))
5056     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5057                                CE_LEFT_BY_PLAYER,
5058                                player->index_bit, leave_side);
5059
5060   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5061                                       CE_PLAYER_LEAVES_X,
5062                                       player->index_bit, leave_side);
5063
5064   Feld[jx][jy] = el_player;
5065   InitPlayerField(jx, jy, el_player, TRUE);
5066
5067   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5068      possible that the relocation target field did not contain a player element,
5069      but a walkable element, to which the new player was relocated -- in this
5070      case, restore that (already initialized!) element on the player field */
5071   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5072   {
5073     Feld[jx][jy] = element;     /* restore previously existing element */
5074   }
5075
5076   /* only visually relocate centered player */
5077   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5078                      FALSE, level.instant_relocation);
5079
5080   TestIfPlayerTouchesBadThing(jx, jy);
5081   TestIfPlayerTouchesCustomElement(jx, jy);
5082
5083   if (IS_CUSTOM_ELEMENT(element))
5084     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5085                                player->index_bit, enter_side);
5086
5087   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5088                                       player->index_bit, enter_side);
5089
5090   if (player->is_switching)
5091   {
5092     /* ensure that relocation while still switching an element does not cause
5093        a new element to be treated as also switched directly after relocation
5094        (this is important for teleporter switches that teleport the player to
5095        a place where another teleporter switch is in the same direction, which
5096        would then incorrectly be treated as immediately switched before the
5097        direction key that caused the switch was released) */
5098
5099     player->switch_x += jx - old_jx;
5100     player->switch_y += jy - old_jy;
5101   }
5102 }
5103
5104 void Explode(int ex, int ey, int phase, int mode)
5105 {
5106   int x, y;
5107   int last_phase;
5108   int border_element;
5109
5110   /* !!! eliminate this variable !!! */
5111   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5112
5113   if (game.explosions_delayed)
5114   {
5115     ExplodeField[ex][ey] = mode;
5116     return;
5117   }
5118
5119   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5120   {
5121     int center_element = Feld[ex][ey];
5122     int artwork_element, explosion_element;     /* set these values later */
5123
5124     /* remove things displayed in background while burning dynamite */
5125     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5126       Back[ex][ey] = 0;
5127
5128     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5129     {
5130       /* put moving element to center field (and let it explode there) */
5131       center_element = MovingOrBlocked2Element(ex, ey);
5132       RemoveMovingField(ex, ey);
5133       Feld[ex][ey] = center_element;
5134     }
5135
5136     /* now "center_element" is finally determined -- set related values now */
5137     artwork_element = center_element;           /* for custom player artwork */
5138     explosion_element = center_element;         /* for custom player artwork */
5139
5140     if (IS_PLAYER(ex, ey))
5141     {
5142       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5143
5144       artwork_element = stored_player[player_nr].artwork_element;
5145
5146       if (level.use_explosion_element[player_nr])
5147       {
5148         explosion_element = level.explosion_element[player_nr];
5149         artwork_element = explosion_element;
5150       }
5151     }
5152
5153     if (mode == EX_TYPE_NORMAL ||
5154         mode == EX_TYPE_CENTER ||
5155         mode == EX_TYPE_CROSS)
5156       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5157
5158     last_phase = element_info[explosion_element].explosion_delay + 1;
5159
5160     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5161     {
5162       int xx = x - ex + 1;
5163       int yy = y - ey + 1;
5164       int element;
5165
5166       if (!IN_LEV_FIELD(x, y) ||
5167           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5168           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5169         continue;
5170
5171       element = Feld[x][y];
5172
5173       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5174       {
5175         element = MovingOrBlocked2Element(x, y);
5176
5177         if (!IS_EXPLOSION_PROOF(element))
5178           RemoveMovingField(x, y);
5179       }
5180
5181       /* indestructible elements can only explode in center (but not flames) */
5182       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5183                                            mode == EX_TYPE_BORDER)) ||
5184           element == EL_FLAMES)
5185         continue;
5186
5187       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5188          behaviour, for example when touching a yamyam that explodes to rocks
5189          with active deadly shield, a rock is created under the player !!! */
5190       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5191 #if 0
5192       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5193           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5194            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5195 #else
5196       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5197 #endif
5198       {
5199         if (IS_ACTIVE_BOMB(element))
5200         {
5201           /* re-activate things under the bomb like gate or penguin */
5202           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5203           Back[x][y] = 0;
5204         }
5205
5206         continue;
5207       }
5208
5209       /* save walkable background elements while explosion on same tile */
5210       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5211           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5212         Back[x][y] = element;
5213
5214       /* ignite explodable elements reached by other explosion */
5215       if (element == EL_EXPLOSION)
5216         element = Store2[x][y];
5217
5218       if (AmoebaNr[x][y] &&
5219           (element == EL_AMOEBA_FULL ||
5220            element == EL_BD_AMOEBA ||
5221            element == EL_AMOEBA_GROWING))
5222       {
5223         AmoebaCnt[AmoebaNr[x][y]]--;
5224         AmoebaCnt2[AmoebaNr[x][y]]--;
5225       }
5226
5227       RemoveField(x, y);
5228
5229       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5230       {
5231         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5232
5233         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5234
5235         if (PLAYERINFO(ex, ey)->use_murphy)
5236           Store[x][y] = EL_EMPTY;
5237       }
5238
5239       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5240          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5241       else if (ELEM_IS_PLAYER(center_element))
5242         Store[x][y] = EL_EMPTY;
5243       else if (center_element == EL_YAMYAM)
5244         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5245       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5246         Store[x][y] = element_info[center_element].content.e[xx][yy];
5247 #if 1
5248       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5249          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5250          otherwise) -- FIX THIS !!! */
5251       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5252         Store[x][y] = element_info[element].content.e[1][1];
5253 #else
5254       else if (!CAN_EXPLODE(element))
5255         Store[x][y] = element_info[element].content.e[1][1];
5256 #endif
5257       else
5258         Store[x][y] = EL_EMPTY;
5259
5260       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5261           center_element == EL_AMOEBA_TO_DIAMOND)
5262         Store2[x][y] = element;
5263
5264       Feld[x][y] = EL_EXPLOSION;
5265       GfxElement[x][y] = artwork_element;
5266
5267       ExplodePhase[x][y] = 1;
5268       ExplodeDelay[x][y] = last_phase;
5269
5270       Stop[x][y] = TRUE;
5271     }
5272
5273     if (center_element == EL_YAMYAM)
5274       game.yamyam_content_nr =
5275         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5276
5277     return;
5278   }
5279
5280   if (Stop[ex][ey])
5281     return;
5282
5283   x = ex;
5284   y = ey;
5285
5286   if (phase == 1)
5287     GfxFrame[x][y] = 0;         /* restart explosion animation */
5288
5289   last_phase = ExplodeDelay[x][y];
5290
5291   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5292
5293   /* this can happen if the player leaves an explosion just in time */
5294   if (GfxElement[x][y] == EL_UNDEFINED)
5295     GfxElement[x][y] = EL_EMPTY;
5296
5297   border_element = Store2[x][y];
5298   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5299     border_element = StorePlayer[x][y];
5300
5301   if (phase == element_info[border_element].ignition_delay ||
5302       phase == last_phase)
5303   {
5304     boolean border_explosion = FALSE;
5305
5306     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5307         !PLAYER_EXPLOSION_PROTECTED(x, y))
5308     {
5309       KillPlayerUnlessExplosionProtected(x, y);
5310       border_explosion = TRUE;
5311     }
5312     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5313     {
5314       Feld[x][y] = Store2[x][y];
5315       Store2[x][y] = 0;
5316       Bang(x, y);
5317       border_explosion = TRUE;
5318     }
5319     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5320     {
5321       AmoebeUmwandeln(x, y);
5322       Store2[x][y] = 0;
5323       border_explosion = TRUE;
5324     }
5325
5326     /* if an element just explodes due to another explosion (chain-reaction),
5327        do not immediately end the new explosion when it was the last frame of
5328        the explosion (as it would be done in the following "if"-statement!) */
5329     if (border_explosion && phase == last_phase)
5330       return;
5331   }
5332
5333   if (phase == last_phase)
5334   {
5335     int element;
5336
5337     element = Feld[x][y] = Store[x][y];
5338     Store[x][y] = Store2[x][y] = 0;
5339     GfxElement[x][y] = EL_UNDEFINED;
5340
5341     /* player can escape from explosions and might therefore be still alive */
5342     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5343         element <= EL_PLAYER_IS_EXPLODING_4)
5344     {
5345       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5346       int explosion_element = EL_PLAYER_1 + player_nr;
5347       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5348       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5349
5350       if (level.use_explosion_element[player_nr])
5351         explosion_element = level.explosion_element[player_nr];
5352
5353       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5354                     element_info[explosion_element].content.e[xx][yy]);
5355     }
5356
5357     /* restore probably existing indestructible background element */
5358     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5359       element = Feld[x][y] = Back[x][y];
5360     Back[x][y] = 0;
5361
5362     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5363     GfxDir[x][y] = MV_NONE;
5364     ChangeDelay[x][y] = 0;
5365     ChangePage[x][y] = -1;
5366
5367     CustomValue[x][y] = 0;
5368
5369     InitField_WithBug2(x, y, FALSE);
5370
5371     TEST_DrawLevelField(x, y);
5372
5373     TestIfElementTouchesCustomElement(x, y);
5374
5375     if (GFX_CRUMBLED(element))
5376       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5377
5378     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5379       StorePlayer[x][y] = 0;
5380
5381     if (ELEM_IS_PLAYER(element))
5382       RelocatePlayer(x, y, element);
5383   }
5384   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5385   {
5386     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5387     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5388
5389     if (phase == delay)
5390       TEST_DrawLevelFieldCrumbled(x, y);
5391
5392     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5393     {
5394       DrawLevelElement(x, y, Back[x][y]);
5395       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5396     }
5397     else if (IS_WALKABLE_UNDER(Back[x][y]))
5398     {
5399       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5400       DrawLevelElementThruMask(x, y, Back[x][y]);
5401     }
5402     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5403       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5404   }
5405 }
5406
5407 void DynaExplode(int ex, int ey)
5408 {
5409   int i, j;
5410   int dynabomb_element = Feld[ex][ey];
5411   int dynabomb_size = 1;
5412   boolean dynabomb_xl = FALSE;
5413   struct PlayerInfo *player;
5414   static int xy[4][2] =
5415   {
5416     { 0, -1 },
5417     { -1, 0 },
5418     { +1, 0 },
5419     { 0, +1 }
5420   };
5421
5422   if (IS_ACTIVE_BOMB(dynabomb_element))
5423   {
5424     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5425     dynabomb_size = player->dynabomb_size;
5426     dynabomb_xl = player->dynabomb_xl;
5427     player->dynabombs_left++;
5428   }
5429
5430   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5431
5432   for (i = 0; i < NUM_DIRECTIONS; i++)
5433   {
5434     for (j = 1; j <= dynabomb_size; j++)
5435     {
5436       int x = ex + j * xy[i][0];
5437       int y = ey + j * xy[i][1];
5438       int element;
5439
5440       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5441         break;
5442
5443       element = Feld[x][y];
5444
5445       /* do not restart explosions of fields with active bombs */
5446       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5447         continue;
5448
5449       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5450
5451       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5452           !IS_DIGGABLE(element) && !dynabomb_xl)
5453         break;
5454     }
5455   }
5456 }
5457
5458 void Bang(int x, int y)
5459 {
5460   int element = MovingOrBlocked2Element(x, y);
5461   int explosion_type = EX_TYPE_NORMAL;
5462
5463   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5464   {
5465     struct PlayerInfo *player = PLAYERINFO(x, y);
5466
5467     element = Feld[x][y] = player->initial_element;
5468
5469     if (level.use_explosion_element[player->index_nr])
5470     {
5471       int explosion_element = level.explosion_element[player->index_nr];
5472
5473       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5474         explosion_type = EX_TYPE_CROSS;
5475       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5476         explosion_type = EX_TYPE_CENTER;
5477     }
5478   }
5479
5480   switch (element)
5481   {
5482     case EL_BUG:
5483     case EL_SPACESHIP:
5484     case EL_BD_BUTTERFLY:
5485     case EL_BD_FIREFLY:
5486     case EL_YAMYAM:
5487     case EL_DARK_YAMYAM:
5488     case EL_ROBOT:
5489     case EL_PACMAN:
5490     case EL_MOLE:
5491       RaiseScoreElement(element);
5492       break;
5493
5494     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5495     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5496     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5497     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5498     case EL_DYNABOMB_INCREASE_NUMBER:
5499     case EL_DYNABOMB_INCREASE_SIZE:
5500     case EL_DYNABOMB_INCREASE_POWER:
5501       explosion_type = EX_TYPE_DYNA;
5502       break;
5503
5504     case EL_DC_LANDMINE:
5505       explosion_type = EX_TYPE_CENTER;
5506       break;
5507
5508     case EL_PENGUIN:
5509     case EL_LAMP:
5510     case EL_LAMP_ACTIVE:
5511     case EL_AMOEBA_TO_DIAMOND:
5512       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5513         explosion_type = EX_TYPE_CENTER;
5514       break;
5515
5516     default:
5517       if (element_info[element].explosion_type == EXPLODES_CROSS)
5518         explosion_type = EX_TYPE_CROSS;
5519       else if (element_info[element].explosion_type == EXPLODES_1X1)
5520         explosion_type = EX_TYPE_CENTER;
5521       break;
5522   }
5523
5524   if (explosion_type == EX_TYPE_DYNA)
5525     DynaExplode(x, y);
5526   else
5527     Explode(x, y, EX_PHASE_START, explosion_type);
5528
5529   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5530 }
5531
5532 void SplashAcid(int x, int y)
5533 {
5534   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5535       (!IN_LEV_FIELD(x - 1, y - 2) ||
5536        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5537     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5538
5539   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5540       (!IN_LEV_FIELD(x + 1, y - 2) ||
5541        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5542     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5543
5544   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5545 }
5546
5547 static void InitBeltMovement()
5548 {
5549   static int belt_base_element[4] =
5550   {
5551     EL_CONVEYOR_BELT_1_LEFT,
5552     EL_CONVEYOR_BELT_2_LEFT,
5553     EL_CONVEYOR_BELT_3_LEFT,
5554     EL_CONVEYOR_BELT_4_LEFT
5555   };
5556   static int belt_base_active_element[4] =
5557   {
5558     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5559     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5560     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5561     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5562   };
5563
5564   int x, y, i, j;
5565
5566   /* set frame order for belt animation graphic according to belt direction */
5567   for (i = 0; i < NUM_BELTS; i++)
5568   {
5569     int belt_nr = i;
5570
5571     for (j = 0; j < NUM_BELT_PARTS; j++)
5572     {
5573       int element = belt_base_active_element[belt_nr] + j;
5574       int graphic_1 = el2img(element);
5575       int graphic_2 = el2panelimg(element);
5576
5577       if (game.belt_dir[i] == MV_LEFT)
5578       {
5579         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5580         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5581       }
5582       else
5583       {
5584         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5585         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5586       }
5587     }
5588   }
5589
5590   SCAN_PLAYFIELD(x, y)
5591   {
5592     int element = Feld[x][y];
5593
5594     for (i = 0; i < NUM_BELTS; i++)
5595     {
5596       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5597       {
5598         int e_belt_nr = getBeltNrFromBeltElement(element);
5599         int belt_nr = i;
5600
5601         if (e_belt_nr == belt_nr)
5602         {
5603           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5604
5605           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5606         }
5607       }
5608     }
5609   }
5610 }
5611
5612 static void ToggleBeltSwitch(int x, int y)
5613 {
5614   static int belt_base_element[4] =
5615   {
5616     EL_CONVEYOR_BELT_1_LEFT,
5617     EL_CONVEYOR_BELT_2_LEFT,
5618     EL_CONVEYOR_BELT_3_LEFT,
5619     EL_CONVEYOR_BELT_4_LEFT
5620   };
5621   static int belt_base_active_element[4] =
5622   {
5623     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5624     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5625     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5626     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5627   };
5628   static int belt_base_switch_element[4] =
5629   {
5630     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5631     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5632     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5633     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5634   };
5635   static int belt_move_dir[4] =
5636   {
5637     MV_LEFT,
5638     MV_NONE,
5639     MV_RIGHT,
5640     MV_NONE,
5641   };
5642
5643   int element = Feld[x][y];
5644   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5645   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5646   int belt_dir = belt_move_dir[belt_dir_nr];
5647   int xx, yy, i;
5648
5649   if (!IS_BELT_SWITCH(element))
5650     return;
5651
5652   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5653   game.belt_dir[belt_nr] = belt_dir;
5654
5655   if (belt_dir_nr == 3)
5656     belt_dir_nr = 1;
5657
5658   /* set frame order for belt animation graphic according to belt direction */
5659   for (i = 0; i < NUM_BELT_PARTS; i++)
5660   {
5661     int element = belt_base_active_element[belt_nr] + i;
5662     int graphic_1 = el2img(element);
5663     int graphic_2 = el2panelimg(element);
5664
5665     if (belt_dir == MV_LEFT)
5666     {
5667       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5668       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5669     }
5670     else
5671     {
5672       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5673       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5674     }
5675   }
5676
5677   SCAN_PLAYFIELD(xx, yy)
5678   {
5679     int element = Feld[xx][yy];
5680
5681     if (IS_BELT_SWITCH(element))
5682     {
5683       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5684
5685       if (e_belt_nr == belt_nr)
5686       {
5687         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5688         TEST_DrawLevelField(xx, yy);
5689       }
5690     }
5691     else if (IS_BELT(element) && belt_dir != MV_NONE)
5692     {
5693       int e_belt_nr = getBeltNrFromBeltElement(element);
5694
5695       if (e_belt_nr == belt_nr)
5696       {
5697         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5698
5699         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5700         TEST_DrawLevelField(xx, yy);
5701       }
5702     }
5703     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5704     {
5705       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5706
5707       if (e_belt_nr == belt_nr)
5708       {
5709         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5710
5711         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5712         TEST_DrawLevelField(xx, yy);
5713       }
5714     }
5715   }
5716 }
5717
5718 static void ToggleSwitchgateSwitch(int x, int y)
5719 {
5720   int xx, yy;
5721
5722   game.switchgate_pos = !game.switchgate_pos;
5723
5724   SCAN_PLAYFIELD(xx, yy)
5725   {
5726     int element = Feld[xx][yy];
5727
5728     if (element == EL_SWITCHGATE_SWITCH_UP)
5729     {
5730       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5731       TEST_DrawLevelField(xx, yy);
5732     }
5733     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5734     {
5735       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5736       TEST_DrawLevelField(xx, yy);
5737     }
5738     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5739     {
5740       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5741       TEST_DrawLevelField(xx, yy);
5742     }
5743     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5744     {
5745       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5746       TEST_DrawLevelField(xx, yy);
5747     }
5748     else if (element == EL_SWITCHGATE_OPEN ||
5749              element == EL_SWITCHGATE_OPENING)
5750     {
5751       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5752
5753       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5754     }
5755     else if (element == EL_SWITCHGATE_CLOSED ||
5756              element == EL_SWITCHGATE_CLOSING)
5757     {
5758       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5759
5760       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5761     }
5762   }
5763 }
5764
5765 static int getInvisibleActiveFromInvisibleElement(int element)
5766 {
5767   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5768           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5769           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5770           element);
5771 }
5772
5773 static int getInvisibleFromInvisibleActiveElement(int element)
5774 {
5775   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5776           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5777           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5778           element);
5779 }
5780
5781 static void RedrawAllLightSwitchesAndInvisibleElements()
5782 {
5783   int x, y;
5784
5785   SCAN_PLAYFIELD(x, y)
5786   {
5787     int element = Feld[x][y];
5788
5789     if (element == EL_LIGHT_SWITCH &&
5790         game.light_time_left > 0)
5791     {
5792       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5793       TEST_DrawLevelField(x, y);
5794     }
5795     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5796              game.light_time_left == 0)
5797     {
5798       Feld[x][y] = EL_LIGHT_SWITCH;
5799       TEST_DrawLevelField(x, y);
5800     }
5801     else if (element == EL_EMC_DRIPPER &&
5802              game.light_time_left > 0)
5803     {
5804       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5805       TEST_DrawLevelField(x, y);
5806     }
5807     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5808              game.light_time_left == 0)
5809     {
5810       Feld[x][y] = EL_EMC_DRIPPER;
5811       TEST_DrawLevelField(x, y);
5812     }
5813     else if (element == EL_INVISIBLE_STEELWALL ||
5814              element == EL_INVISIBLE_WALL ||
5815              element == EL_INVISIBLE_SAND)
5816     {
5817       if (game.light_time_left > 0)
5818         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5819
5820       TEST_DrawLevelField(x, y);
5821
5822       /* uncrumble neighbour fields, if needed */
5823       if (element == EL_INVISIBLE_SAND)
5824         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5825     }
5826     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5827              element == EL_INVISIBLE_WALL_ACTIVE ||
5828              element == EL_INVISIBLE_SAND_ACTIVE)
5829     {
5830       if (game.light_time_left == 0)
5831         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5832
5833       TEST_DrawLevelField(x, y);
5834
5835       /* re-crumble neighbour fields, if needed */
5836       if (element == EL_INVISIBLE_SAND)
5837         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5838     }
5839   }
5840 }
5841
5842 static void RedrawAllInvisibleElementsForLenses()
5843 {
5844   int x, y;
5845
5846   SCAN_PLAYFIELD(x, y)
5847   {
5848     int element = Feld[x][y];
5849
5850     if (element == EL_EMC_DRIPPER &&
5851         game.lenses_time_left > 0)
5852     {
5853       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5854       TEST_DrawLevelField(x, y);
5855     }
5856     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5857              game.lenses_time_left == 0)
5858     {
5859       Feld[x][y] = EL_EMC_DRIPPER;
5860       TEST_DrawLevelField(x, y);
5861     }
5862     else if (element == EL_INVISIBLE_STEELWALL ||
5863              element == EL_INVISIBLE_WALL ||
5864              element == EL_INVISIBLE_SAND)
5865     {
5866       if (game.lenses_time_left > 0)
5867         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5868
5869       TEST_DrawLevelField(x, y);
5870
5871       /* uncrumble neighbour fields, if needed */
5872       if (element == EL_INVISIBLE_SAND)
5873         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5874     }
5875     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5876              element == EL_INVISIBLE_WALL_ACTIVE ||
5877              element == EL_INVISIBLE_SAND_ACTIVE)
5878     {
5879       if (game.lenses_time_left == 0)
5880         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5881
5882       TEST_DrawLevelField(x, y);
5883
5884       /* re-crumble neighbour fields, if needed */
5885       if (element == EL_INVISIBLE_SAND)
5886         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5887     }
5888   }
5889 }
5890
5891 static void RedrawAllInvisibleElementsForMagnifier()
5892 {
5893   int x, y;
5894
5895   SCAN_PLAYFIELD(x, y)
5896   {
5897     int element = Feld[x][y];
5898
5899     if (element == EL_EMC_FAKE_GRASS &&
5900         game.magnify_time_left > 0)
5901     {
5902       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5903       TEST_DrawLevelField(x, y);
5904     }
5905     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5906              game.magnify_time_left == 0)
5907     {
5908       Feld[x][y] = EL_EMC_FAKE_GRASS;
5909       TEST_DrawLevelField(x, y);
5910     }
5911     else if (IS_GATE_GRAY(element) &&
5912              game.magnify_time_left > 0)
5913     {
5914       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5915                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5916                     IS_EM_GATE_GRAY(element) ?
5917                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5918                     IS_EMC_GATE_GRAY(element) ?
5919                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5920                     IS_DC_GATE_GRAY(element) ?
5921                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5922                     element);
5923       TEST_DrawLevelField(x, y);
5924     }
5925     else if (IS_GATE_GRAY_ACTIVE(element) &&
5926              game.magnify_time_left == 0)
5927     {
5928       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5929                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5930                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5931                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5932                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5933                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5934                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5935                     EL_DC_GATE_WHITE_GRAY :
5936                     element);
5937       TEST_DrawLevelField(x, y);
5938     }
5939   }
5940 }
5941
5942 static void ToggleLightSwitch(int x, int y)
5943 {
5944   int element = Feld[x][y];
5945
5946   game.light_time_left =
5947     (element == EL_LIGHT_SWITCH ?
5948      level.time_light * FRAMES_PER_SECOND : 0);
5949
5950   RedrawAllLightSwitchesAndInvisibleElements();
5951 }
5952
5953 static void ActivateTimegateSwitch(int x, int y)
5954 {
5955   int xx, yy;
5956
5957   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5958
5959   SCAN_PLAYFIELD(xx, yy)
5960   {
5961     int element = Feld[xx][yy];
5962
5963     if (element == EL_TIMEGATE_CLOSED ||
5964         element == EL_TIMEGATE_CLOSING)
5965     {
5966       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5967       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5968     }
5969
5970     /*
5971     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5972     {
5973       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5974       TEST_DrawLevelField(xx, yy);
5975     }
5976     */
5977
5978   }
5979
5980   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5981                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5982 }
5983
5984 void Impact(int x, int y)
5985 {
5986   boolean last_line = (y == lev_fieldy - 1);
5987   boolean object_hit = FALSE;
5988   boolean impact = (last_line || object_hit);
5989   int element = Feld[x][y];
5990   int smashed = EL_STEELWALL;
5991
5992   if (!last_line)       /* check if element below was hit */
5993   {
5994     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5995       return;
5996
5997     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5998                                          MovDir[x][y + 1] != MV_DOWN ||
5999                                          MovPos[x][y + 1] <= TILEY / 2));
6000
6001     /* do not smash moving elements that left the smashed field in time */
6002     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6003         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6004       object_hit = FALSE;
6005
6006 #if USE_QUICKSAND_IMPACT_BUGFIX
6007     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6008     {
6009       RemoveMovingField(x, y + 1);
6010       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6011       Feld[x][y + 2] = EL_ROCK;
6012       TEST_DrawLevelField(x, y + 2);
6013
6014       object_hit = TRUE;
6015     }
6016
6017     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6018     {
6019       RemoveMovingField(x, y + 1);
6020       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6021       Feld[x][y + 2] = EL_ROCK;
6022       TEST_DrawLevelField(x, y + 2);
6023
6024       object_hit = TRUE;
6025     }
6026 #endif
6027
6028     if (object_hit)
6029       smashed = MovingOrBlocked2Element(x, y + 1);
6030
6031     impact = (last_line || object_hit);
6032   }
6033
6034   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6035   {
6036     SplashAcid(x, y + 1);
6037     return;
6038   }
6039
6040   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6041   /* only reset graphic animation if graphic really changes after impact */
6042   if (impact &&
6043       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6044   {
6045     ResetGfxAnimation(x, y);
6046     TEST_DrawLevelField(x, y);
6047   }
6048
6049   if (impact && CAN_EXPLODE_IMPACT(element))
6050   {
6051     Bang(x, y);
6052     return;
6053   }
6054   else if (impact && element == EL_PEARL &&
6055            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6056   {
6057     ResetGfxAnimation(x, y);
6058
6059     Feld[x][y] = EL_PEARL_BREAKING;
6060     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6061     return;
6062   }
6063   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6064   {
6065     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6066
6067     return;
6068   }
6069
6070   if (impact && element == EL_AMOEBA_DROP)
6071   {
6072     if (object_hit && IS_PLAYER(x, y + 1))
6073       KillPlayerUnlessEnemyProtected(x, y + 1);
6074     else if (object_hit && smashed == EL_PENGUIN)
6075       Bang(x, y + 1);
6076     else
6077     {
6078       Feld[x][y] = EL_AMOEBA_GROWING;
6079       Store[x][y] = EL_AMOEBA_WET;
6080
6081       ResetRandomAnimationValue(x, y);
6082     }
6083     return;
6084   }
6085
6086   if (object_hit)               /* check which object was hit */
6087   {
6088     if ((CAN_PASS_MAGIC_WALL(element) && 
6089          (smashed == EL_MAGIC_WALL ||
6090           smashed == EL_BD_MAGIC_WALL)) ||
6091         (CAN_PASS_DC_MAGIC_WALL(element) &&
6092          smashed == EL_DC_MAGIC_WALL))
6093     {
6094       int xx, yy;
6095       int activated_magic_wall =
6096         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6097          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6098          EL_DC_MAGIC_WALL_ACTIVE);
6099
6100       /* activate magic wall / mill */
6101       SCAN_PLAYFIELD(xx, yy)
6102       {
6103         if (Feld[xx][yy] == smashed)
6104           Feld[xx][yy] = activated_magic_wall;
6105       }
6106
6107       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6108       game.magic_wall_active = TRUE;
6109
6110       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6111                             SND_MAGIC_WALL_ACTIVATING :
6112                             smashed == EL_BD_MAGIC_WALL ?
6113                             SND_BD_MAGIC_WALL_ACTIVATING :
6114                             SND_DC_MAGIC_WALL_ACTIVATING));
6115     }
6116
6117     if (IS_PLAYER(x, y + 1))
6118     {
6119       if (CAN_SMASH_PLAYER(element))
6120       {
6121         KillPlayerUnlessEnemyProtected(x, y + 1);
6122         return;
6123       }
6124     }
6125     else if (smashed == EL_PENGUIN)
6126     {
6127       if (CAN_SMASH_PLAYER(element))
6128       {
6129         Bang(x, y + 1);
6130         return;
6131       }
6132     }
6133     else if (element == EL_BD_DIAMOND)
6134     {
6135       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6136       {
6137         Bang(x, y + 1);
6138         return;
6139       }
6140     }
6141     else if (((element == EL_SP_INFOTRON ||
6142                element == EL_SP_ZONK) &&
6143               (smashed == EL_SP_SNIKSNAK ||
6144                smashed == EL_SP_ELECTRON ||
6145                smashed == EL_SP_DISK_ORANGE)) ||
6146              (element == EL_SP_INFOTRON &&
6147               smashed == EL_SP_DISK_YELLOW))
6148     {
6149       Bang(x, y + 1);
6150       return;
6151     }
6152     else if (CAN_SMASH_EVERYTHING(element))
6153     {
6154       if (IS_CLASSIC_ENEMY(smashed) ||
6155           CAN_EXPLODE_SMASHED(smashed))
6156       {
6157         Bang(x, y + 1);
6158         return;
6159       }
6160       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6161       {
6162         if (smashed == EL_LAMP ||
6163             smashed == EL_LAMP_ACTIVE)
6164         {
6165           Bang(x, y + 1);
6166           return;
6167         }
6168         else if (smashed == EL_NUT)
6169         {
6170           Feld[x][y + 1] = EL_NUT_BREAKING;
6171           PlayLevelSound(x, y, SND_NUT_BREAKING);
6172           RaiseScoreElement(EL_NUT);
6173           return;
6174         }
6175         else if (smashed == EL_PEARL)
6176         {
6177           ResetGfxAnimation(x, y);
6178
6179           Feld[x][y + 1] = EL_PEARL_BREAKING;
6180           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6181           return;
6182         }
6183         else if (smashed == EL_DIAMOND)
6184         {
6185           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6186           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6187           return;
6188         }
6189         else if (IS_BELT_SWITCH(smashed))
6190         {
6191           ToggleBeltSwitch(x, y + 1);
6192         }
6193         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6194                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6195                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6196                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6197         {
6198           ToggleSwitchgateSwitch(x, y + 1);
6199         }
6200         else if (smashed == EL_LIGHT_SWITCH ||
6201                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6202         {
6203           ToggleLightSwitch(x, y + 1);
6204         }
6205         else
6206         {
6207           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6208
6209           CheckElementChangeBySide(x, y + 1, smashed, element,
6210                                    CE_SWITCHED, CH_SIDE_TOP);
6211           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6212                                             CH_SIDE_TOP);
6213         }
6214       }
6215       else
6216       {
6217         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6218       }
6219     }
6220   }
6221
6222   /* play sound of magic wall / mill */
6223   if (!last_line &&
6224       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6225        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6226        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6227   {
6228     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6229       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6230     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6231       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6232     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6233       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6234
6235     return;
6236   }
6237
6238   /* play sound of object that hits the ground */
6239   if (last_line || object_hit)
6240     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6241 }
6242
6243 inline static void TurnRoundExt(int x, int y)
6244 {
6245   static struct
6246   {
6247     int dx, dy;
6248   } move_xy[] =
6249   {
6250     {  0,  0 },
6251     { -1,  0 },
6252     { +1,  0 },
6253     {  0,  0 },
6254     {  0, -1 },
6255     {  0,  0 }, { 0, 0 }, { 0, 0 },
6256     {  0, +1 }
6257   };
6258   static struct
6259   {
6260     int left, right, back;
6261   } turn[] =
6262   {
6263     { 0,        0,              0        },
6264     { MV_DOWN,  MV_UP,          MV_RIGHT },
6265     { MV_UP,    MV_DOWN,        MV_LEFT  },
6266     { 0,        0,              0        },
6267     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6268     { 0,        0,              0        },
6269     { 0,        0,              0        },
6270     { 0,        0,              0        },
6271     { MV_RIGHT, MV_LEFT,        MV_UP    }
6272   };
6273
6274   int element = Feld[x][y];
6275   int move_pattern = element_info[element].move_pattern;
6276
6277   int old_move_dir = MovDir[x][y];
6278   int left_dir  = turn[old_move_dir].left;
6279   int right_dir = turn[old_move_dir].right;
6280   int back_dir  = turn[old_move_dir].back;
6281
6282   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6283   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6284   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6285   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6286
6287   int left_x  = x + left_dx,  left_y  = y + left_dy;
6288   int right_x = x + right_dx, right_y = y + right_dy;
6289   int move_x  = x + move_dx,  move_y  = y + move_dy;
6290
6291   int xx, yy;
6292
6293   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6294   {
6295     TestIfBadThingTouchesOtherBadThing(x, y);
6296
6297     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6298       MovDir[x][y] = right_dir;
6299     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6300       MovDir[x][y] = left_dir;
6301
6302     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6303       MovDelay[x][y] = 9;
6304     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6305       MovDelay[x][y] = 1;
6306   }
6307   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6308   {
6309     TestIfBadThingTouchesOtherBadThing(x, y);
6310
6311     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6312       MovDir[x][y] = left_dir;
6313     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6314       MovDir[x][y] = right_dir;
6315
6316     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6317       MovDelay[x][y] = 9;
6318     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6319       MovDelay[x][y] = 1;
6320   }
6321   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6322   {
6323     TestIfBadThingTouchesOtherBadThing(x, y);
6324
6325     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6326       MovDir[x][y] = left_dir;
6327     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6328       MovDir[x][y] = right_dir;
6329
6330     if (MovDir[x][y] != old_move_dir)
6331       MovDelay[x][y] = 9;
6332   }
6333   else if (element == EL_YAMYAM)
6334   {
6335     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6336     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6337
6338     if (can_turn_left && can_turn_right)
6339       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6340     else if (can_turn_left)
6341       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6342     else if (can_turn_right)
6343       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6344     else
6345       MovDir[x][y] = back_dir;
6346
6347     MovDelay[x][y] = 16 + 16 * RND(3);
6348   }
6349   else if (element == EL_DARK_YAMYAM)
6350   {
6351     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6352                                                          left_x, left_y);
6353     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6354                                                          right_x, right_y);
6355
6356     if (can_turn_left && can_turn_right)
6357       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6358     else if (can_turn_left)
6359       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6360     else if (can_turn_right)
6361       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6362     else
6363       MovDir[x][y] = back_dir;
6364
6365     MovDelay[x][y] = 16 + 16 * RND(3);
6366   }
6367   else if (element == EL_PACMAN)
6368   {
6369     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6370     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6371
6372     if (can_turn_left && can_turn_right)
6373       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6374     else if (can_turn_left)
6375       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6376     else if (can_turn_right)
6377       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6378     else
6379       MovDir[x][y] = back_dir;
6380
6381     MovDelay[x][y] = 6 + RND(40);
6382   }
6383   else if (element == EL_PIG)
6384   {
6385     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6386     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6387     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6388     boolean should_turn_left, should_turn_right, should_move_on;
6389     int rnd_value = 24;
6390     int rnd = RND(rnd_value);
6391
6392     should_turn_left = (can_turn_left &&
6393                         (!can_move_on ||
6394                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6395                                                    y + back_dy + left_dy)));
6396     should_turn_right = (can_turn_right &&
6397                          (!can_move_on ||
6398                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6399                                                     y + back_dy + right_dy)));
6400     should_move_on = (can_move_on &&
6401                       (!can_turn_left ||
6402                        !can_turn_right ||
6403                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6404                                                  y + move_dy + left_dy) ||
6405                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6406                                                  y + move_dy + right_dy)));
6407
6408     if (should_turn_left || should_turn_right || should_move_on)
6409     {
6410       if (should_turn_left && should_turn_right && should_move_on)
6411         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6412                         rnd < 2 * rnd_value / 3 ? right_dir :
6413                         old_move_dir);
6414       else if (should_turn_left && should_turn_right)
6415         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6416       else if (should_turn_left && should_move_on)
6417         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6418       else if (should_turn_right && should_move_on)
6419         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6420       else if (should_turn_left)
6421         MovDir[x][y] = left_dir;
6422       else if (should_turn_right)
6423         MovDir[x][y] = right_dir;
6424       else if (should_move_on)
6425         MovDir[x][y] = old_move_dir;
6426     }
6427     else if (can_move_on && rnd > rnd_value / 8)
6428       MovDir[x][y] = old_move_dir;
6429     else if (can_turn_left && can_turn_right)
6430       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6431     else if (can_turn_left && rnd > rnd_value / 8)
6432       MovDir[x][y] = left_dir;
6433     else if (can_turn_right && rnd > rnd_value/8)
6434       MovDir[x][y] = right_dir;
6435     else
6436       MovDir[x][y] = back_dir;
6437
6438     xx = x + move_xy[MovDir[x][y]].dx;
6439     yy = y + move_xy[MovDir[x][y]].dy;
6440
6441     if (!IN_LEV_FIELD(xx, yy) ||
6442         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6443       MovDir[x][y] = old_move_dir;
6444
6445     MovDelay[x][y] = 0;
6446   }
6447   else if (element == EL_DRAGON)
6448   {
6449     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6450     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6451     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6452     int rnd_value = 24;
6453     int rnd = RND(rnd_value);
6454
6455     if (can_move_on && rnd > rnd_value / 8)
6456       MovDir[x][y] = old_move_dir;
6457     else if (can_turn_left && can_turn_right)
6458       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6459     else if (can_turn_left && rnd > rnd_value / 8)
6460       MovDir[x][y] = left_dir;
6461     else if (can_turn_right && rnd > rnd_value / 8)
6462       MovDir[x][y] = right_dir;
6463     else
6464       MovDir[x][y] = back_dir;
6465
6466     xx = x + move_xy[MovDir[x][y]].dx;
6467     yy = y + move_xy[MovDir[x][y]].dy;
6468
6469     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6470       MovDir[x][y] = old_move_dir;
6471
6472     MovDelay[x][y] = 0;
6473   }
6474   else if (element == EL_MOLE)
6475   {
6476     boolean can_move_on =
6477       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6478                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6479                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6480     if (!can_move_on)
6481     {
6482       boolean can_turn_left =
6483         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6484                               IS_AMOEBOID(Feld[left_x][left_y])));
6485
6486       boolean can_turn_right =
6487         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6488                               IS_AMOEBOID(Feld[right_x][right_y])));
6489
6490       if (can_turn_left && can_turn_right)
6491         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6492       else if (can_turn_left)
6493         MovDir[x][y] = left_dir;
6494       else
6495         MovDir[x][y] = right_dir;
6496     }
6497
6498     if (MovDir[x][y] != old_move_dir)
6499       MovDelay[x][y] = 9;
6500   }
6501   else if (element == EL_BALLOON)
6502   {
6503     MovDir[x][y] = game.wind_direction;
6504     MovDelay[x][y] = 0;
6505   }
6506   else if (element == EL_SPRING)
6507   {
6508     if (MovDir[x][y] & MV_HORIZONTAL)
6509     {
6510       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6511           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6512       {
6513         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6514         ResetGfxAnimation(move_x, move_y);
6515         TEST_DrawLevelField(move_x, move_y);
6516
6517         MovDir[x][y] = back_dir;
6518       }
6519       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6520                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6521         MovDir[x][y] = MV_NONE;
6522     }
6523
6524     MovDelay[x][y] = 0;
6525   }
6526   else if (element == EL_ROBOT ||
6527            element == EL_SATELLITE ||
6528            element == EL_PENGUIN ||
6529            element == EL_EMC_ANDROID)
6530   {
6531     int attr_x = -1, attr_y = -1;
6532
6533     if (AllPlayersGone)
6534     {
6535       attr_x = ExitX;
6536       attr_y = ExitY;
6537     }
6538     else
6539     {
6540       int i;
6541
6542       for (i = 0; i < MAX_PLAYERS; i++)
6543       {
6544         struct PlayerInfo *player = &stored_player[i];
6545         int jx = player->jx, jy = player->jy;
6546
6547         if (!player->active)
6548           continue;
6549
6550         if (attr_x == -1 ||
6551             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6552         {
6553           attr_x = jx;
6554           attr_y = jy;
6555         }
6556       }
6557     }
6558
6559     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6560         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6561          game.engine_version < VERSION_IDENT(3,1,0,0)))
6562     {
6563       attr_x = ZX;
6564       attr_y = ZY;
6565     }
6566
6567     if (element == EL_PENGUIN)
6568     {
6569       int i;
6570       static int xy[4][2] =
6571       {
6572         { 0, -1 },
6573         { -1, 0 },
6574         { +1, 0 },
6575         { 0, +1 }
6576       };
6577
6578       for (i = 0; i < NUM_DIRECTIONS; i++)
6579       {
6580         int ex = x + xy[i][0];
6581         int ey = y + xy[i][1];
6582
6583         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6584                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6585                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6586                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6587         {
6588           attr_x = ex;
6589           attr_y = ey;
6590           break;
6591         }
6592       }
6593     }
6594
6595     MovDir[x][y] = MV_NONE;
6596     if (attr_x < x)
6597       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6598     else if (attr_x > x)
6599       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6600     if (attr_y < y)
6601       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6602     else if (attr_y > y)
6603       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6604
6605     if (element == EL_ROBOT)
6606     {
6607       int newx, newy;
6608
6609       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6610         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6611       Moving2Blocked(x, y, &newx, &newy);
6612
6613       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6614         MovDelay[x][y] = 8 + 8 * !RND(3);
6615       else
6616         MovDelay[x][y] = 16;
6617     }
6618     else if (element == EL_PENGUIN)
6619     {
6620       int newx, newy;
6621
6622       MovDelay[x][y] = 1;
6623
6624       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6625       {
6626         boolean first_horiz = RND(2);
6627         int new_move_dir = MovDir[x][y];
6628
6629         MovDir[x][y] =
6630           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6631         Moving2Blocked(x, y, &newx, &newy);
6632
6633         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6634           return;
6635
6636         MovDir[x][y] =
6637           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6638         Moving2Blocked(x, y, &newx, &newy);
6639
6640         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6641           return;
6642
6643         MovDir[x][y] = old_move_dir;
6644         return;
6645       }
6646     }
6647     else if (element == EL_SATELLITE)
6648     {
6649       int newx, newy;
6650
6651       MovDelay[x][y] = 1;
6652
6653       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6654       {
6655         boolean first_horiz = RND(2);
6656         int new_move_dir = MovDir[x][y];
6657
6658         MovDir[x][y] =
6659           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6660         Moving2Blocked(x, y, &newx, &newy);
6661
6662         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6663           return;
6664
6665         MovDir[x][y] =
6666           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6667         Moving2Blocked(x, y, &newx, &newy);
6668
6669         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6670           return;
6671
6672         MovDir[x][y] = old_move_dir;
6673         return;
6674       }
6675     }
6676     else if (element == EL_EMC_ANDROID)
6677     {
6678       static int check_pos[16] =
6679       {
6680         -1,             /*  0 => (invalid)          */
6681         7,              /*  1 => MV_LEFT            */
6682         3,              /*  2 => MV_RIGHT           */
6683         -1,             /*  3 => (invalid)          */
6684         1,              /*  4 =>            MV_UP   */
6685         0,              /*  5 => MV_LEFT  | MV_UP   */
6686         2,              /*  6 => MV_RIGHT | MV_UP   */
6687         -1,             /*  7 => (invalid)          */
6688         5,              /*  8 =>            MV_DOWN */
6689         6,              /*  9 => MV_LEFT  | MV_DOWN */
6690         4,              /* 10 => MV_RIGHT | MV_DOWN */
6691         -1,             /* 11 => (invalid)          */
6692         -1,             /* 12 => (invalid)          */
6693         -1,             /* 13 => (invalid)          */
6694         -1,             /* 14 => (invalid)          */
6695         -1,             /* 15 => (invalid)          */
6696       };
6697       static struct
6698       {
6699         int dx, dy;
6700         int dir;
6701       } check_xy[8] =
6702       {
6703         { -1, -1,       MV_LEFT  | MV_UP   },
6704         {  0, -1,                  MV_UP   },
6705         { +1, -1,       MV_RIGHT | MV_UP   },
6706         { +1,  0,       MV_RIGHT           },
6707         { +1, +1,       MV_RIGHT | MV_DOWN },
6708         {  0, +1,                  MV_DOWN },
6709         { -1, +1,       MV_LEFT  | MV_DOWN },
6710         { -1,  0,       MV_LEFT            },
6711       };
6712       int start_pos, check_order;
6713       boolean can_clone = FALSE;
6714       int i;
6715
6716       /* check if there is any free field around current position */
6717       for (i = 0; i < 8; i++)
6718       {
6719         int newx = x + check_xy[i].dx;
6720         int newy = y + check_xy[i].dy;
6721
6722         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6723         {
6724           can_clone = TRUE;
6725
6726           break;
6727         }
6728       }
6729
6730       if (can_clone)            /* randomly find an element to clone */
6731       {
6732         can_clone = FALSE;
6733
6734         start_pos = check_pos[RND(8)];
6735         check_order = (RND(2) ? -1 : +1);
6736
6737         for (i = 0; i < 8; i++)
6738         {
6739           int pos_raw = start_pos + i * check_order;
6740           int pos = (pos_raw + 8) % 8;
6741           int newx = x + check_xy[pos].dx;
6742           int newy = y + check_xy[pos].dy;
6743
6744           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6745           {
6746             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6747             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6748
6749             Store[x][y] = Feld[newx][newy];
6750
6751             can_clone = TRUE;
6752
6753             break;
6754           }
6755         }
6756       }
6757
6758       if (can_clone)            /* randomly find a direction to move */
6759       {
6760         can_clone = FALSE;
6761
6762         start_pos = check_pos[RND(8)];
6763         check_order = (RND(2) ? -1 : +1);
6764
6765         for (i = 0; i < 8; i++)
6766         {
6767           int pos_raw = start_pos + i * check_order;
6768           int pos = (pos_raw + 8) % 8;
6769           int newx = x + check_xy[pos].dx;
6770           int newy = y + check_xy[pos].dy;
6771           int new_move_dir = check_xy[pos].dir;
6772
6773           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6774           {
6775             MovDir[x][y] = new_move_dir;
6776             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6777
6778             can_clone = TRUE;
6779
6780             break;
6781           }
6782         }
6783       }
6784
6785       if (can_clone)            /* cloning and moving successful */
6786         return;
6787
6788       /* cannot clone -- try to move towards player */
6789
6790       start_pos = check_pos[MovDir[x][y] & 0x0f];
6791       check_order = (RND(2) ? -1 : +1);
6792
6793       for (i = 0; i < 3; i++)
6794       {
6795         /* first check start_pos, then previous/next or (next/previous) pos */
6796         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6797         int pos = (pos_raw + 8) % 8;
6798         int newx = x + check_xy[pos].dx;
6799         int newy = y + check_xy[pos].dy;
6800         int new_move_dir = check_xy[pos].dir;
6801
6802         if (IS_PLAYER(newx, newy))
6803           break;
6804
6805         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6806         {
6807           MovDir[x][y] = new_move_dir;
6808           MovDelay[x][y] = level.android_move_time * 8 + 1;
6809
6810           break;
6811         }
6812       }
6813     }
6814   }
6815   else if (move_pattern == MV_TURNING_LEFT ||
6816            move_pattern == MV_TURNING_RIGHT ||
6817            move_pattern == MV_TURNING_LEFT_RIGHT ||
6818            move_pattern == MV_TURNING_RIGHT_LEFT ||
6819            move_pattern == MV_TURNING_RANDOM ||
6820            move_pattern == MV_ALL_DIRECTIONS)
6821   {
6822     boolean can_turn_left =
6823       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6824     boolean can_turn_right =
6825       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6826
6827     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6828       return;
6829
6830     if (move_pattern == MV_TURNING_LEFT)
6831       MovDir[x][y] = left_dir;
6832     else if (move_pattern == MV_TURNING_RIGHT)
6833       MovDir[x][y] = right_dir;
6834     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6835       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6836     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6837       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6838     else if (move_pattern == MV_TURNING_RANDOM)
6839       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6840                       can_turn_right && !can_turn_left ? right_dir :
6841                       RND(2) ? left_dir : right_dir);
6842     else if (can_turn_left && can_turn_right)
6843       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6844     else if (can_turn_left)
6845       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6846     else if (can_turn_right)
6847       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6848     else
6849       MovDir[x][y] = back_dir;
6850
6851     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6852   }
6853   else if (move_pattern == MV_HORIZONTAL ||
6854            move_pattern == MV_VERTICAL)
6855   {
6856     if (move_pattern & old_move_dir)
6857       MovDir[x][y] = back_dir;
6858     else if (move_pattern == MV_HORIZONTAL)
6859       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6860     else if (move_pattern == MV_VERTICAL)
6861       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6862
6863     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6864   }
6865   else if (move_pattern & MV_ANY_DIRECTION)
6866   {
6867     MovDir[x][y] = move_pattern;
6868     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6869   }
6870   else if (move_pattern & MV_WIND_DIRECTION)
6871   {
6872     MovDir[x][y] = game.wind_direction;
6873     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6874   }
6875   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6876   {
6877     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6878       MovDir[x][y] = left_dir;
6879     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6880       MovDir[x][y] = right_dir;
6881
6882     if (MovDir[x][y] != old_move_dir)
6883       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6884   }
6885   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6886   {
6887     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6888       MovDir[x][y] = right_dir;
6889     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6890       MovDir[x][y] = left_dir;
6891
6892     if (MovDir[x][y] != old_move_dir)
6893       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894   }
6895   else if (move_pattern == MV_TOWARDS_PLAYER ||
6896            move_pattern == MV_AWAY_FROM_PLAYER)
6897   {
6898     int attr_x = -1, attr_y = -1;
6899     int newx, newy;
6900     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6901
6902     if (AllPlayersGone)
6903     {
6904       attr_x = ExitX;
6905       attr_y = ExitY;
6906     }
6907     else
6908     {
6909       int i;
6910
6911       for (i = 0; i < MAX_PLAYERS; i++)
6912       {
6913         struct PlayerInfo *player = &stored_player[i];
6914         int jx = player->jx, jy = player->jy;
6915
6916         if (!player->active)
6917           continue;
6918
6919         if (attr_x == -1 ||
6920             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6921         {
6922           attr_x = jx;
6923           attr_y = jy;
6924         }
6925       }
6926     }
6927
6928     MovDir[x][y] = MV_NONE;
6929     if (attr_x < x)
6930       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6931     else if (attr_x > x)
6932       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6933     if (attr_y < y)
6934       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6935     else if (attr_y > y)
6936       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6937
6938     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6939
6940     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6941     {
6942       boolean first_horiz = RND(2);
6943       int new_move_dir = MovDir[x][y];
6944
6945       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6946       {
6947         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6948         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6949
6950         return;
6951       }
6952
6953       MovDir[x][y] =
6954         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6955       Moving2Blocked(x, y, &newx, &newy);
6956
6957       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6958         return;
6959
6960       MovDir[x][y] =
6961         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6962       Moving2Blocked(x, y, &newx, &newy);
6963
6964       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6965         return;
6966
6967       MovDir[x][y] = old_move_dir;
6968     }
6969   }
6970   else if (move_pattern == MV_WHEN_PUSHED ||
6971            move_pattern == MV_WHEN_DROPPED)
6972   {
6973     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6974       MovDir[x][y] = MV_NONE;
6975
6976     MovDelay[x][y] = 0;
6977   }
6978   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6979   {
6980     static int test_xy[7][2] =
6981     {
6982       { 0, -1 },
6983       { -1, 0 },
6984       { +1, 0 },
6985       { 0, +1 },
6986       { 0, -1 },
6987       { -1, 0 },
6988       { +1, 0 },
6989     };
6990     static int test_dir[7] =
6991     {
6992       MV_UP,
6993       MV_LEFT,
6994       MV_RIGHT,
6995       MV_DOWN,
6996       MV_UP,
6997       MV_LEFT,
6998       MV_RIGHT,
6999     };
7000     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7001     int move_preference = -1000000;     /* start with very low preference */
7002     int new_move_dir = MV_NONE;
7003     int start_test = RND(4);
7004     int i;
7005
7006     for (i = 0; i < NUM_DIRECTIONS; i++)
7007     {
7008       int move_dir = test_dir[start_test + i];
7009       int move_dir_preference;
7010
7011       xx = x + test_xy[start_test + i][0];
7012       yy = y + test_xy[start_test + i][1];
7013
7014       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7015           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7016       {
7017         new_move_dir = move_dir;
7018
7019         break;
7020       }
7021
7022       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7023         continue;
7024
7025       move_dir_preference = -1 * RunnerVisit[xx][yy];
7026       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7027         move_dir_preference = PlayerVisit[xx][yy];
7028
7029       if (move_dir_preference > move_preference)
7030       {
7031         /* prefer field that has not been visited for the longest time */
7032         move_preference = move_dir_preference;
7033         new_move_dir = move_dir;
7034       }
7035       else if (move_dir_preference == move_preference &&
7036                move_dir == old_move_dir)
7037       {
7038         /* prefer last direction when all directions are preferred equally */
7039         move_preference = move_dir_preference;
7040         new_move_dir = move_dir;
7041       }
7042     }
7043
7044     MovDir[x][y] = new_move_dir;
7045     if (old_move_dir != new_move_dir)
7046       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7047   }
7048 }
7049
7050 static void TurnRound(int x, int y)
7051 {
7052   int direction = MovDir[x][y];
7053
7054   TurnRoundExt(x, y);
7055
7056   GfxDir[x][y] = MovDir[x][y];
7057
7058   if (direction != MovDir[x][y])
7059     GfxFrame[x][y] = 0;
7060
7061   if (MovDelay[x][y])
7062     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7063
7064   ResetGfxFrame(x, y, FALSE);
7065 }
7066
7067 static boolean JustBeingPushed(int x, int y)
7068 {
7069   int i;
7070
7071   for (i = 0; i < MAX_PLAYERS; i++)
7072   {
7073     struct PlayerInfo *player = &stored_player[i];
7074
7075     if (player->active && player->is_pushing && player->MovPos)
7076     {
7077       int next_jx = player->jx + (player->jx - player->last_jx);
7078       int next_jy = player->jy + (player->jy - player->last_jy);
7079
7080       if (x == next_jx && y == next_jy)
7081         return TRUE;
7082     }
7083   }
7084
7085   return FALSE;
7086 }
7087
7088 void StartMoving(int x, int y)
7089 {
7090   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7091   int element = Feld[x][y];
7092
7093   if (Stop[x][y])
7094     return;
7095
7096   if (MovDelay[x][y] == 0)
7097     GfxAction[x][y] = ACTION_DEFAULT;
7098
7099   if (CAN_FALL(element) && y < lev_fieldy - 1)
7100   {
7101     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7102         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7103       if (JustBeingPushed(x, y))
7104         return;
7105
7106     if (element == EL_QUICKSAND_FULL)
7107     {
7108       if (IS_FREE(x, y + 1))
7109       {
7110         InitMovingField(x, y, MV_DOWN);
7111         started_moving = TRUE;
7112
7113         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7114 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7115         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7116           Store[x][y] = EL_ROCK;
7117 #else
7118         Store[x][y] = EL_ROCK;
7119 #endif
7120
7121         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7122       }
7123       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7124       {
7125         if (!MovDelay[x][y])
7126         {
7127           MovDelay[x][y] = TILEY + 1;
7128
7129           ResetGfxAnimation(x, y);
7130           ResetGfxAnimation(x, y + 1);
7131         }
7132
7133         if (MovDelay[x][y])
7134         {
7135           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7136           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7137
7138           MovDelay[x][y]--;
7139           if (MovDelay[x][y])
7140             return;
7141         }
7142
7143         Feld[x][y] = EL_QUICKSAND_EMPTY;
7144         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7145         Store[x][y + 1] = Store[x][y];
7146         Store[x][y] = 0;
7147
7148         PlayLevelSoundAction(x, y, ACTION_FILLING);
7149       }
7150       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7151       {
7152         if (!MovDelay[x][y])
7153         {
7154           MovDelay[x][y] = TILEY + 1;
7155
7156           ResetGfxAnimation(x, y);
7157           ResetGfxAnimation(x, y + 1);
7158         }
7159
7160         if (MovDelay[x][y])
7161         {
7162           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7163           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7164
7165           MovDelay[x][y]--;
7166           if (MovDelay[x][y])
7167             return;
7168         }
7169
7170         Feld[x][y] = EL_QUICKSAND_EMPTY;
7171         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7172         Store[x][y + 1] = Store[x][y];
7173         Store[x][y] = 0;
7174
7175         PlayLevelSoundAction(x, y, ACTION_FILLING);
7176       }
7177     }
7178     else if (element == EL_QUICKSAND_FAST_FULL)
7179     {
7180       if (IS_FREE(x, y + 1))
7181       {
7182         InitMovingField(x, y, MV_DOWN);
7183         started_moving = TRUE;
7184
7185         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7186 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7187         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7188           Store[x][y] = EL_ROCK;
7189 #else
7190         Store[x][y] = EL_ROCK;
7191 #endif
7192
7193         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7194       }
7195       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7196       {
7197         if (!MovDelay[x][y])
7198         {
7199           MovDelay[x][y] = TILEY + 1;
7200
7201           ResetGfxAnimation(x, y);
7202           ResetGfxAnimation(x, y + 1);
7203         }
7204
7205         if (MovDelay[x][y])
7206         {
7207           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7208           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7209
7210           MovDelay[x][y]--;
7211           if (MovDelay[x][y])
7212             return;
7213         }
7214
7215         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7216         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7217         Store[x][y + 1] = Store[x][y];
7218         Store[x][y] = 0;
7219
7220         PlayLevelSoundAction(x, y, ACTION_FILLING);
7221       }
7222       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7223       {
7224         if (!MovDelay[x][y])
7225         {
7226           MovDelay[x][y] = TILEY + 1;
7227
7228           ResetGfxAnimation(x, y);
7229           ResetGfxAnimation(x, y + 1);
7230         }
7231
7232         if (MovDelay[x][y])
7233         {
7234           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7235           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7236
7237           MovDelay[x][y]--;
7238           if (MovDelay[x][y])
7239             return;
7240         }
7241
7242         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7243         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7244         Store[x][y + 1] = Store[x][y];
7245         Store[x][y] = 0;
7246
7247         PlayLevelSoundAction(x, y, ACTION_FILLING);
7248       }
7249     }
7250     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7251              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7252     {
7253       InitMovingField(x, y, MV_DOWN);
7254       started_moving = TRUE;
7255
7256       Feld[x][y] = EL_QUICKSAND_FILLING;
7257       Store[x][y] = element;
7258
7259       PlayLevelSoundAction(x, y, ACTION_FILLING);
7260     }
7261     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7262              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7263     {
7264       InitMovingField(x, y, MV_DOWN);
7265       started_moving = TRUE;
7266
7267       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7268       Store[x][y] = element;
7269
7270       PlayLevelSoundAction(x, y, ACTION_FILLING);
7271     }
7272     else if (element == EL_MAGIC_WALL_FULL)
7273     {
7274       if (IS_FREE(x, y + 1))
7275       {
7276         InitMovingField(x, y, MV_DOWN);
7277         started_moving = TRUE;
7278
7279         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7280         Store[x][y] = EL_CHANGED(Store[x][y]);
7281       }
7282       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7283       {
7284         if (!MovDelay[x][y])
7285           MovDelay[x][y] = TILEY / 4 + 1;
7286
7287         if (MovDelay[x][y])
7288         {
7289           MovDelay[x][y]--;
7290           if (MovDelay[x][y])
7291             return;
7292         }
7293
7294         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7295         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7296         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7297         Store[x][y] = 0;
7298       }
7299     }
7300     else if (element == EL_BD_MAGIC_WALL_FULL)
7301     {
7302       if (IS_FREE(x, y + 1))
7303       {
7304         InitMovingField(x, y, MV_DOWN);
7305         started_moving = TRUE;
7306
7307         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7308         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7309       }
7310       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7311       {
7312         if (!MovDelay[x][y])
7313           MovDelay[x][y] = TILEY / 4 + 1;
7314
7315         if (MovDelay[x][y])
7316         {
7317           MovDelay[x][y]--;
7318           if (MovDelay[x][y])
7319             return;
7320         }
7321
7322         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7323         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7324         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7325         Store[x][y] = 0;
7326       }
7327     }
7328     else if (element == EL_DC_MAGIC_WALL_FULL)
7329     {
7330       if (IS_FREE(x, y + 1))
7331       {
7332         InitMovingField(x, y, MV_DOWN);
7333         started_moving = TRUE;
7334
7335         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7336         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7337       }
7338       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7339       {
7340         if (!MovDelay[x][y])
7341           MovDelay[x][y] = TILEY / 4 + 1;
7342
7343         if (MovDelay[x][y])
7344         {
7345           MovDelay[x][y]--;
7346           if (MovDelay[x][y])
7347             return;
7348         }
7349
7350         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7351         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7352         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7353         Store[x][y] = 0;
7354       }
7355     }
7356     else if ((CAN_PASS_MAGIC_WALL(element) &&
7357               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7358                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7359              (CAN_PASS_DC_MAGIC_WALL(element) &&
7360               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7361
7362     {
7363       InitMovingField(x, y, MV_DOWN);
7364       started_moving = TRUE;
7365
7366       Feld[x][y] =
7367         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7368          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7369          EL_DC_MAGIC_WALL_FILLING);
7370       Store[x][y] = element;
7371     }
7372     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7373     {
7374       SplashAcid(x, y + 1);
7375
7376       InitMovingField(x, y, MV_DOWN);
7377       started_moving = TRUE;
7378
7379       Store[x][y] = EL_ACID;
7380     }
7381     else if (
7382              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7383               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7384              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7385               CAN_FALL(element) && WasJustFalling[x][y] &&
7386               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7387
7388              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7389               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7390               (Feld[x][y + 1] == EL_BLOCKED)))
7391     {
7392       /* this is needed for a special case not covered by calling "Impact()"
7393          from "ContinueMoving()": if an element moves to a tile directly below
7394          another element which was just falling on that tile (which was empty
7395          in the previous frame), the falling element above would just stop
7396          instead of smashing the element below (in previous version, the above
7397          element was just checked for "moving" instead of "falling", resulting
7398          in incorrect smashes caused by horizontal movement of the above
7399          element; also, the case of the player being the element to smash was
7400          simply not covered here... :-/ ) */
7401
7402       CheckCollision[x][y] = 0;
7403       CheckImpact[x][y] = 0;
7404
7405       Impact(x, y);
7406     }
7407     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7408     {
7409       if (MovDir[x][y] == MV_NONE)
7410       {
7411         InitMovingField(x, y, MV_DOWN);
7412         started_moving = TRUE;
7413       }
7414     }
7415     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7416     {
7417       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7418         MovDir[x][y] = MV_DOWN;
7419
7420       InitMovingField(x, y, MV_DOWN);
7421       started_moving = TRUE;
7422     }
7423     else if (element == EL_AMOEBA_DROP)
7424     {
7425       Feld[x][y] = EL_AMOEBA_GROWING;
7426       Store[x][y] = EL_AMOEBA_WET;
7427     }
7428     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7429               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7430              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7431              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7432     {
7433       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7434                                 (IS_FREE(x - 1, y + 1) ||
7435                                  Feld[x - 1][y + 1] == EL_ACID));
7436       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7437                                 (IS_FREE(x + 1, y + 1) ||
7438                                  Feld[x + 1][y + 1] == EL_ACID));
7439       boolean can_fall_any  = (can_fall_left || can_fall_right);
7440       boolean can_fall_both = (can_fall_left && can_fall_right);
7441       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7442
7443       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7444       {
7445         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7446           can_fall_right = FALSE;
7447         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7448           can_fall_left = FALSE;
7449         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7450           can_fall_right = FALSE;
7451         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7452           can_fall_left = FALSE;
7453
7454         can_fall_any  = (can_fall_left || can_fall_right);
7455         can_fall_both = FALSE;
7456       }
7457
7458       if (can_fall_both)
7459       {
7460         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7461           can_fall_right = FALSE;       /* slip down on left side */
7462         else
7463           can_fall_left = !(can_fall_right = RND(2));
7464
7465         can_fall_both = FALSE;
7466       }
7467
7468       if (can_fall_any)
7469       {
7470         /* if not determined otherwise, prefer left side for slipping down */
7471         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7472         started_moving = TRUE;
7473       }
7474     }
7475     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7476     {
7477       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7478       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7479       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7480       int belt_dir = game.belt_dir[belt_nr];
7481
7482       if ((belt_dir == MV_LEFT  && left_is_free) ||
7483           (belt_dir == MV_RIGHT && right_is_free))
7484       {
7485         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7486
7487         InitMovingField(x, y, belt_dir);
7488         started_moving = TRUE;
7489
7490         Pushed[x][y] = TRUE;
7491         Pushed[nextx][y] = TRUE;
7492
7493         GfxAction[x][y] = ACTION_DEFAULT;
7494       }
7495       else
7496       {
7497         MovDir[x][y] = 0;       /* if element was moving, stop it */
7498       }
7499     }
7500   }
7501
7502   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7503   if (CAN_MOVE(element) && !started_moving)
7504   {
7505     int move_pattern = element_info[element].move_pattern;
7506     int newx, newy;
7507
7508     Moving2Blocked(x, y, &newx, &newy);
7509
7510     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7511       return;
7512
7513     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7514         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7515     {
7516       WasJustMoving[x][y] = 0;
7517       CheckCollision[x][y] = 0;
7518
7519       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7520
7521       if (Feld[x][y] != element)        /* element has changed */
7522         return;
7523     }
7524
7525     if (!MovDelay[x][y])        /* start new movement phase */
7526     {
7527       /* all objects that can change their move direction after each step
7528          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7529
7530       if (element != EL_YAMYAM &&
7531           element != EL_DARK_YAMYAM &&
7532           element != EL_PACMAN &&
7533           !(move_pattern & MV_ANY_DIRECTION) &&
7534           move_pattern != MV_TURNING_LEFT &&
7535           move_pattern != MV_TURNING_RIGHT &&
7536           move_pattern != MV_TURNING_LEFT_RIGHT &&
7537           move_pattern != MV_TURNING_RIGHT_LEFT &&
7538           move_pattern != MV_TURNING_RANDOM)
7539       {
7540         TurnRound(x, y);
7541
7542         if (MovDelay[x][y] && (element == EL_BUG ||
7543                                element == EL_SPACESHIP ||
7544                                element == EL_SP_SNIKSNAK ||
7545                                element == EL_SP_ELECTRON ||
7546                                element == EL_MOLE))
7547           TEST_DrawLevelField(x, y);
7548       }
7549     }
7550
7551     if (MovDelay[x][y])         /* wait some time before next movement */
7552     {
7553       MovDelay[x][y]--;
7554
7555       if (element == EL_ROBOT ||
7556           element == EL_YAMYAM ||
7557           element == EL_DARK_YAMYAM)
7558       {
7559         DrawLevelElementAnimationIfNeeded(x, y, element);
7560         PlayLevelSoundAction(x, y, ACTION_WAITING);
7561       }
7562       else if (element == EL_SP_ELECTRON)
7563         DrawLevelElementAnimationIfNeeded(x, y, element);
7564       else if (element == EL_DRAGON)
7565       {
7566         int i;
7567         int dir = MovDir[x][y];
7568         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7569         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7570         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7571                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7572                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7573                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7574         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7575
7576         GfxAction[x][y] = ACTION_ATTACKING;
7577
7578         if (IS_PLAYER(x, y))
7579           DrawPlayerField(x, y);
7580         else
7581           TEST_DrawLevelField(x, y);
7582
7583         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7584
7585         for (i = 1; i <= 3; i++)
7586         {
7587           int xx = x + i * dx;
7588           int yy = y + i * dy;
7589           int sx = SCREENX(xx);
7590           int sy = SCREENY(yy);
7591           int flame_graphic = graphic + (i - 1);
7592
7593           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7594             break;
7595
7596           if (MovDelay[x][y])
7597           {
7598             int flamed = MovingOrBlocked2Element(xx, yy);
7599
7600             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7601               Bang(xx, yy);
7602             else
7603               RemoveMovingField(xx, yy);
7604
7605             ChangeDelay[xx][yy] = 0;
7606
7607             Feld[xx][yy] = EL_FLAMES;
7608
7609             if (IN_SCR_FIELD(sx, sy))
7610             {
7611               TEST_DrawLevelFieldCrumbled(xx, yy);
7612               DrawGraphic(sx, sy, flame_graphic, frame);
7613             }
7614           }
7615           else
7616           {
7617             if (Feld[xx][yy] == EL_FLAMES)
7618               Feld[xx][yy] = EL_EMPTY;
7619             TEST_DrawLevelField(xx, yy);
7620           }
7621         }
7622       }
7623
7624       if (MovDelay[x][y])       /* element still has to wait some time */
7625       {
7626         PlayLevelSoundAction(x, y, ACTION_WAITING);
7627
7628         return;
7629       }
7630     }
7631
7632     /* now make next step */
7633
7634     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7635
7636     if (DONT_COLLIDE_WITH(element) &&
7637         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7638         !PLAYER_ENEMY_PROTECTED(newx, newy))
7639     {
7640       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7641
7642       return;
7643     }
7644
7645     else if (CAN_MOVE_INTO_ACID(element) &&
7646              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7647              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7648              (MovDir[x][y] == MV_DOWN ||
7649               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7650     {
7651       SplashAcid(newx, newy);
7652       Store[x][y] = EL_ACID;
7653     }
7654     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7655     {
7656       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7657           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7658           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7659           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7660       {
7661         RemoveField(x, y);
7662         TEST_DrawLevelField(x, y);
7663
7664         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7665         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7666           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7667
7668         local_player->friends_still_needed--;
7669         if (!local_player->friends_still_needed &&
7670             !local_player->GameOver && AllPlayersGone)
7671           PlayerWins(local_player);
7672
7673         return;
7674       }
7675       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7676       {
7677         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7678           TEST_DrawLevelField(newx, newy);
7679         else
7680           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7681       }
7682       else if (!IS_FREE(newx, newy))
7683       {
7684         GfxAction[x][y] = ACTION_WAITING;
7685
7686         if (IS_PLAYER(x, y))
7687           DrawPlayerField(x, y);
7688         else
7689           TEST_DrawLevelField(x, y);
7690
7691         return;
7692       }
7693     }
7694     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7695     {
7696       if (IS_FOOD_PIG(Feld[newx][newy]))
7697       {
7698         if (IS_MOVING(newx, newy))
7699           RemoveMovingField(newx, newy);
7700         else
7701         {
7702           Feld[newx][newy] = EL_EMPTY;
7703           TEST_DrawLevelField(newx, newy);
7704         }
7705
7706         PlayLevelSound(x, y, SND_PIG_DIGGING);
7707       }
7708       else if (!IS_FREE(newx, newy))
7709       {
7710         if (IS_PLAYER(x, y))
7711           DrawPlayerField(x, y);
7712         else
7713           TEST_DrawLevelField(x, y);
7714
7715         return;
7716       }
7717     }
7718     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7719     {
7720       if (Store[x][y] != EL_EMPTY)
7721       {
7722         boolean can_clone = FALSE;
7723         int xx, yy;
7724
7725         /* check if element to clone is still there */
7726         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7727         {
7728           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7729           {
7730             can_clone = TRUE;
7731
7732             break;
7733           }
7734         }
7735
7736         /* cannot clone or target field not free anymore -- do not clone */
7737         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7738           Store[x][y] = EL_EMPTY;
7739       }
7740
7741       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7742       {
7743         if (IS_MV_DIAGONAL(MovDir[x][y]))
7744         {
7745           int diagonal_move_dir = MovDir[x][y];
7746           int stored = Store[x][y];
7747           int change_delay = 8;
7748           int graphic;
7749
7750           /* android is moving diagonally */
7751
7752           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7753
7754           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7755           GfxElement[x][y] = EL_EMC_ANDROID;
7756           GfxAction[x][y] = ACTION_SHRINKING;
7757           GfxDir[x][y] = diagonal_move_dir;
7758           ChangeDelay[x][y] = change_delay;
7759
7760           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7761                                    GfxDir[x][y]);
7762
7763           DrawLevelGraphicAnimation(x, y, graphic);
7764           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7765
7766           if (Feld[newx][newy] == EL_ACID)
7767           {
7768             SplashAcid(newx, newy);
7769
7770             return;
7771           }
7772
7773           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7774
7775           Store[newx][newy] = EL_EMC_ANDROID;
7776           GfxElement[newx][newy] = EL_EMC_ANDROID;
7777           GfxAction[newx][newy] = ACTION_GROWING;
7778           GfxDir[newx][newy] = diagonal_move_dir;
7779           ChangeDelay[newx][newy] = change_delay;
7780
7781           graphic = el_act_dir2img(GfxElement[newx][newy],
7782                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7783
7784           DrawLevelGraphicAnimation(newx, newy, graphic);
7785           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7786
7787           return;
7788         }
7789         else
7790         {
7791           Feld[newx][newy] = EL_EMPTY;
7792           TEST_DrawLevelField(newx, newy);
7793
7794           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7795         }
7796       }
7797       else if (!IS_FREE(newx, newy))
7798       {
7799         return;
7800       }
7801     }
7802     else if (IS_CUSTOM_ELEMENT(element) &&
7803              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7804     {
7805       if (!DigFieldByCE(newx, newy, element))
7806         return;
7807
7808       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7809       {
7810         RunnerVisit[x][y] = FrameCounter;
7811         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7812       }
7813     }
7814     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7815     {
7816       if (!IS_FREE(newx, newy))
7817       {
7818         if (IS_PLAYER(x, y))
7819           DrawPlayerField(x, y);
7820         else
7821           TEST_DrawLevelField(x, y);
7822
7823         return;
7824       }
7825       else
7826       {
7827         boolean wanna_flame = !RND(10);
7828         int dx = newx - x, dy = newy - y;
7829         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7830         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7831         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7832                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7833         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7834                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7835
7836         if ((wanna_flame ||
7837              IS_CLASSIC_ENEMY(element1) ||
7838              IS_CLASSIC_ENEMY(element2)) &&
7839             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7840             element1 != EL_FLAMES && element2 != EL_FLAMES)
7841         {
7842           ResetGfxAnimation(x, y);
7843           GfxAction[x][y] = ACTION_ATTACKING;
7844
7845           if (IS_PLAYER(x, y))
7846             DrawPlayerField(x, y);
7847           else
7848             TEST_DrawLevelField(x, y);
7849
7850           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7851
7852           MovDelay[x][y] = 50;
7853
7854           Feld[newx][newy] = EL_FLAMES;
7855           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7856             Feld[newx1][newy1] = EL_FLAMES;
7857           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7858             Feld[newx2][newy2] = EL_FLAMES;
7859
7860           return;
7861         }
7862       }
7863     }
7864     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7865              Feld[newx][newy] == EL_DIAMOND)
7866     {
7867       if (IS_MOVING(newx, newy))
7868         RemoveMovingField(newx, newy);
7869       else
7870       {
7871         Feld[newx][newy] = EL_EMPTY;
7872         TEST_DrawLevelField(newx, newy);
7873       }
7874
7875       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7876     }
7877     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7878              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7879     {
7880       if (AmoebaNr[newx][newy])
7881       {
7882         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7883         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7884             Feld[newx][newy] == EL_BD_AMOEBA)
7885           AmoebaCnt[AmoebaNr[newx][newy]]--;
7886       }
7887
7888       if (IS_MOVING(newx, newy))
7889       {
7890         RemoveMovingField(newx, newy);
7891       }
7892       else
7893       {
7894         Feld[newx][newy] = EL_EMPTY;
7895         TEST_DrawLevelField(newx, newy);
7896       }
7897
7898       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7899     }
7900     else if ((element == EL_PACMAN || element == EL_MOLE)
7901              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7902     {
7903       if (AmoebaNr[newx][newy])
7904       {
7905         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7906         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7907             Feld[newx][newy] == EL_BD_AMOEBA)
7908           AmoebaCnt[AmoebaNr[newx][newy]]--;
7909       }
7910
7911       if (element == EL_MOLE)
7912       {
7913         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7914         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7915
7916         ResetGfxAnimation(x, y);
7917         GfxAction[x][y] = ACTION_DIGGING;
7918         TEST_DrawLevelField(x, y);
7919
7920         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7921
7922         return;                         /* wait for shrinking amoeba */
7923       }
7924       else      /* element == EL_PACMAN */
7925       {
7926         Feld[newx][newy] = EL_EMPTY;
7927         TEST_DrawLevelField(newx, newy);
7928         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7929       }
7930     }
7931     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7932              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7933               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7934     {
7935       /* wait for shrinking amoeba to completely disappear */
7936       return;
7937     }
7938     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7939     {
7940       /* object was running against a wall */
7941
7942       TurnRound(x, y);
7943
7944       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7945         DrawLevelElementAnimation(x, y, element);
7946
7947       if (DONT_TOUCH(element))
7948         TestIfBadThingTouchesPlayer(x, y);
7949
7950       return;
7951     }
7952
7953     InitMovingField(x, y, MovDir[x][y]);
7954
7955     PlayLevelSoundAction(x, y, ACTION_MOVING);
7956   }
7957
7958   if (MovDir[x][y])
7959     ContinueMoving(x, y);
7960 }
7961
7962 void ContinueMoving(int x, int y)
7963 {
7964   int element = Feld[x][y];
7965   struct ElementInfo *ei = &element_info[element];
7966   int direction = MovDir[x][y];
7967   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7968   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7969   int newx = x + dx, newy = y + dy;
7970   int stored = Store[x][y];
7971   int stored_new = Store[newx][newy];
7972   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7973   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7974   boolean last_line = (newy == lev_fieldy - 1);
7975
7976   MovPos[x][y] += getElementMoveStepsize(x, y);
7977
7978   if (pushed_by_player) /* special case: moving object pushed by player */
7979     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7980
7981   if (ABS(MovPos[x][y]) < TILEX)
7982   {
7983     TEST_DrawLevelField(x, y);
7984
7985     return;     /* element is still moving */
7986   }
7987
7988   /* element reached destination field */
7989
7990   Feld[x][y] = EL_EMPTY;
7991   Feld[newx][newy] = element;
7992   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7993
7994   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7995   {
7996     element = Feld[newx][newy] = EL_ACID;
7997   }
7998   else if (element == EL_MOLE)
7999   {
8000     Feld[x][y] = EL_SAND;
8001
8002     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8003   }
8004   else if (element == EL_QUICKSAND_FILLING)
8005   {
8006     element = Feld[newx][newy] = get_next_element(element);
8007     Store[newx][newy] = Store[x][y];
8008   }
8009   else if (element == EL_QUICKSAND_EMPTYING)
8010   {
8011     Feld[x][y] = get_next_element(element);
8012     element = Feld[newx][newy] = Store[x][y];
8013   }
8014   else if (element == EL_QUICKSAND_FAST_FILLING)
8015   {
8016     element = Feld[newx][newy] = get_next_element(element);
8017     Store[newx][newy] = Store[x][y];
8018   }
8019   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8020   {
8021     Feld[x][y] = get_next_element(element);
8022     element = Feld[newx][newy] = Store[x][y];
8023   }
8024   else if (element == EL_MAGIC_WALL_FILLING)
8025   {
8026     element = Feld[newx][newy] = get_next_element(element);
8027     if (!game.magic_wall_active)
8028       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8029     Store[newx][newy] = Store[x][y];
8030   }
8031   else if (element == EL_MAGIC_WALL_EMPTYING)
8032   {
8033     Feld[x][y] = get_next_element(element);
8034     if (!game.magic_wall_active)
8035       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8036     element = Feld[newx][newy] = Store[x][y];
8037
8038     InitField(newx, newy, FALSE);
8039   }
8040   else if (element == EL_BD_MAGIC_WALL_FILLING)
8041   {
8042     element = Feld[newx][newy] = get_next_element(element);
8043     if (!game.magic_wall_active)
8044       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8045     Store[newx][newy] = Store[x][y];
8046   }
8047   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8048   {
8049     Feld[x][y] = get_next_element(element);
8050     if (!game.magic_wall_active)
8051       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8052     element = Feld[newx][newy] = Store[x][y];
8053
8054     InitField(newx, newy, FALSE);
8055   }
8056   else if (element == EL_DC_MAGIC_WALL_FILLING)
8057   {
8058     element = Feld[newx][newy] = get_next_element(element);
8059     if (!game.magic_wall_active)
8060       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8061     Store[newx][newy] = Store[x][y];
8062   }
8063   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8064   {
8065     Feld[x][y] = get_next_element(element);
8066     if (!game.magic_wall_active)
8067       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8068     element = Feld[newx][newy] = Store[x][y];
8069
8070     InitField(newx, newy, FALSE);
8071   }
8072   else if (element == EL_AMOEBA_DROPPING)
8073   {
8074     Feld[x][y] = get_next_element(element);
8075     element = Feld[newx][newy] = Store[x][y];
8076   }
8077   else if (element == EL_SOKOBAN_OBJECT)
8078   {
8079     if (Back[x][y])
8080       Feld[x][y] = Back[x][y];
8081
8082     if (Back[newx][newy])
8083       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8084
8085     Back[x][y] = Back[newx][newy] = 0;
8086   }
8087
8088   Store[x][y] = EL_EMPTY;
8089   MovPos[x][y] = 0;
8090   MovDir[x][y] = 0;
8091   MovDelay[x][y] = 0;
8092
8093   MovDelay[newx][newy] = 0;
8094
8095   if (CAN_CHANGE_OR_HAS_ACTION(element))
8096   {
8097     /* copy element change control values to new field */
8098     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8099     ChangePage[newx][newy]  = ChangePage[x][y];
8100     ChangeCount[newx][newy] = ChangeCount[x][y];
8101     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8102   }
8103
8104   CustomValue[newx][newy] = CustomValue[x][y];
8105
8106   ChangeDelay[x][y] = 0;
8107   ChangePage[x][y] = -1;
8108   ChangeCount[x][y] = 0;
8109   ChangeEvent[x][y] = -1;
8110
8111   CustomValue[x][y] = 0;
8112
8113   /* copy animation control values to new field */
8114   GfxFrame[newx][newy]  = GfxFrame[x][y];
8115   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8116   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8117   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8118
8119   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8120
8121   /* some elements can leave other elements behind after moving */
8122   if (ei->move_leave_element != EL_EMPTY &&
8123       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8124       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8125   {
8126     int move_leave_element = ei->move_leave_element;
8127
8128     /* this makes it possible to leave the removed element again */
8129     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8130       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8131
8132     Feld[x][y] = move_leave_element;
8133
8134     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8135       MovDir[x][y] = direction;
8136
8137     InitField(x, y, FALSE);
8138
8139     if (GFX_CRUMBLED(Feld[x][y]))
8140       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8141
8142     if (ELEM_IS_PLAYER(move_leave_element))
8143       RelocatePlayer(x, y, move_leave_element);
8144   }
8145
8146   /* do this after checking for left-behind element */
8147   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8148
8149   if (!CAN_MOVE(element) ||
8150       (CAN_FALL(element) && direction == MV_DOWN &&
8151        (element == EL_SPRING ||
8152         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8153         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8154     GfxDir[x][y] = MovDir[newx][newy] = 0;
8155
8156   TEST_DrawLevelField(x, y);
8157   TEST_DrawLevelField(newx, newy);
8158
8159   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8160
8161   /* prevent pushed element from moving on in pushed direction */
8162   if (pushed_by_player && CAN_MOVE(element) &&
8163       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8164       !(element_info[element].move_pattern & direction))
8165     TurnRound(newx, newy);
8166
8167   /* prevent elements on conveyor belt from moving on in last direction */
8168   if (pushed_by_conveyor && CAN_FALL(element) &&
8169       direction & MV_HORIZONTAL)
8170     MovDir[newx][newy] = 0;
8171
8172   if (!pushed_by_player)
8173   {
8174     int nextx = newx + dx, nexty = newy + dy;
8175     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8176
8177     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8178
8179     if (CAN_FALL(element) && direction == MV_DOWN)
8180       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8181
8182     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8183       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8184
8185     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8186       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8187   }
8188
8189   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8190   {
8191     TestIfBadThingTouchesPlayer(newx, newy);
8192     TestIfBadThingTouchesFriend(newx, newy);
8193
8194     if (!IS_CUSTOM_ELEMENT(element))
8195       TestIfBadThingTouchesOtherBadThing(newx, newy);
8196   }
8197   else if (element == EL_PENGUIN)
8198     TestIfFriendTouchesBadThing(newx, newy);
8199
8200   if (DONT_GET_HIT_BY(element))
8201   {
8202     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8203   }
8204
8205   /* give the player one last chance (one more frame) to move away */
8206   if (CAN_FALL(element) && direction == MV_DOWN &&
8207       (last_line || (!IS_FREE(x, newy + 1) &&
8208                      (!IS_PLAYER(x, newy + 1) ||
8209                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8210     Impact(x, newy);
8211
8212   if (pushed_by_player && !game.use_change_when_pushing_bug)
8213   {
8214     int push_side = MV_DIR_OPPOSITE(direction);
8215     struct PlayerInfo *player = PLAYERINFO(x, y);
8216
8217     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8218                                player->index_bit, push_side);
8219     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8220                                         player->index_bit, push_side);
8221   }
8222
8223   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8224     MovDelay[newx][newy] = 1;
8225
8226   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8227
8228   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8229   TestIfElementHitsCustomElement(newx, newy, direction);
8230   TestIfPlayerTouchesCustomElement(newx, newy);
8231   TestIfElementTouchesCustomElement(newx, newy);
8232
8233   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8234       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8235     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8236                              MV_DIR_OPPOSITE(direction));
8237 }
8238
8239 int AmoebeNachbarNr(int ax, int ay)
8240 {
8241   int i;
8242   int element = Feld[ax][ay];
8243   int group_nr = 0;
8244   static int xy[4][2] =
8245   {
8246     { 0, -1 },
8247     { -1, 0 },
8248     { +1, 0 },
8249     { 0, +1 }
8250   };
8251
8252   for (i = 0; i < NUM_DIRECTIONS; i++)
8253   {
8254     int x = ax + xy[i][0];
8255     int y = ay + xy[i][1];
8256
8257     if (!IN_LEV_FIELD(x, y))
8258       continue;
8259
8260     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8261       group_nr = AmoebaNr[x][y];
8262   }
8263
8264   return group_nr;
8265 }
8266
8267 void AmoebenVereinigen(int ax, int ay)
8268 {
8269   int i, x, y, xx, yy;
8270   int new_group_nr = AmoebaNr[ax][ay];
8271   static int xy[4][2] =
8272   {
8273     { 0, -1 },
8274     { -1, 0 },
8275     { +1, 0 },
8276     { 0, +1 }
8277   };
8278
8279   if (new_group_nr == 0)
8280     return;
8281
8282   for (i = 0; i < NUM_DIRECTIONS; i++)
8283   {
8284     x = ax + xy[i][0];
8285     y = ay + xy[i][1];
8286
8287     if (!IN_LEV_FIELD(x, y))
8288       continue;
8289
8290     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8291          Feld[x][y] == EL_BD_AMOEBA ||
8292          Feld[x][y] == EL_AMOEBA_DEAD) &&
8293         AmoebaNr[x][y] != new_group_nr)
8294     {
8295       int old_group_nr = AmoebaNr[x][y];
8296
8297       if (old_group_nr == 0)
8298         return;
8299
8300       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8301       AmoebaCnt[old_group_nr] = 0;
8302       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8303       AmoebaCnt2[old_group_nr] = 0;
8304
8305       SCAN_PLAYFIELD(xx, yy)
8306       {
8307         if (AmoebaNr[xx][yy] == old_group_nr)
8308           AmoebaNr[xx][yy] = new_group_nr;
8309       }
8310     }
8311   }
8312 }
8313
8314 void AmoebeUmwandeln(int ax, int ay)
8315 {
8316   int i, x, y;
8317
8318   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8319   {
8320     int group_nr = AmoebaNr[ax][ay];
8321
8322 #ifdef DEBUG
8323     if (group_nr == 0)
8324     {
8325       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8326       printf("AmoebeUmwandeln(): This should never happen!\n");
8327       return;
8328     }
8329 #endif
8330
8331     SCAN_PLAYFIELD(x, y)
8332     {
8333       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8334       {
8335         AmoebaNr[x][y] = 0;
8336         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8337       }
8338     }
8339
8340     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8341                             SND_AMOEBA_TURNING_TO_GEM :
8342                             SND_AMOEBA_TURNING_TO_ROCK));
8343     Bang(ax, ay);
8344   }
8345   else
8346   {
8347     static int xy[4][2] =
8348     {
8349       { 0, -1 },
8350       { -1, 0 },
8351       { +1, 0 },
8352       { 0, +1 }
8353     };
8354
8355     for (i = 0; i < NUM_DIRECTIONS; i++)
8356     {
8357       x = ax + xy[i][0];
8358       y = ay + xy[i][1];
8359
8360       if (!IN_LEV_FIELD(x, y))
8361         continue;
8362
8363       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8364       {
8365         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8366                               SND_AMOEBA_TURNING_TO_GEM :
8367                               SND_AMOEBA_TURNING_TO_ROCK));
8368         Bang(x, y);
8369       }
8370     }
8371   }
8372 }
8373
8374 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8375 {
8376   int x, y;
8377   int group_nr = AmoebaNr[ax][ay];
8378   boolean done = FALSE;
8379
8380 #ifdef DEBUG
8381   if (group_nr == 0)
8382   {
8383     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8384     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8385     return;
8386   }
8387 #endif
8388
8389   SCAN_PLAYFIELD(x, y)
8390   {
8391     if (AmoebaNr[x][y] == group_nr &&
8392         (Feld[x][y] == EL_AMOEBA_DEAD ||
8393          Feld[x][y] == EL_BD_AMOEBA ||
8394          Feld[x][y] == EL_AMOEBA_GROWING))
8395     {
8396       AmoebaNr[x][y] = 0;
8397       Feld[x][y] = new_element;
8398       InitField(x, y, FALSE);
8399       TEST_DrawLevelField(x, y);
8400       done = TRUE;
8401     }
8402   }
8403
8404   if (done)
8405     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8406                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8407                             SND_BD_AMOEBA_TURNING_TO_GEM));
8408 }
8409
8410 void AmoebeWaechst(int x, int y)
8411 {
8412   static unsigned int sound_delay = 0;
8413   static unsigned int sound_delay_value = 0;
8414
8415   if (!MovDelay[x][y])          /* start new growing cycle */
8416   {
8417     MovDelay[x][y] = 7;
8418
8419     if (DelayReached(&sound_delay, sound_delay_value))
8420     {
8421       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8422       sound_delay_value = 30;
8423     }
8424   }
8425
8426   if (MovDelay[x][y])           /* wait some time before growing bigger */
8427   {
8428     MovDelay[x][y]--;
8429     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8430     {
8431       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8432                                            6 - MovDelay[x][y]);
8433
8434       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8435     }
8436
8437     if (!MovDelay[x][y])
8438     {
8439       Feld[x][y] = Store[x][y];
8440       Store[x][y] = 0;
8441       TEST_DrawLevelField(x, y);
8442     }
8443   }
8444 }
8445
8446 void AmoebaDisappearing(int x, int y)
8447 {
8448   static unsigned int sound_delay = 0;
8449   static unsigned int sound_delay_value = 0;
8450
8451   if (!MovDelay[x][y])          /* start new shrinking cycle */
8452   {
8453     MovDelay[x][y] = 7;
8454
8455     if (DelayReached(&sound_delay, sound_delay_value))
8456       sound_delay_value = 30;
8457   }
8458
8459   if (MovDelay[x][y])           /* wait some time before shrinking */
8460   {
8461     MovDelay[x][y]--;
8462     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8463     {
8464       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8465                                            6 - MovDelay[x][y]);
8466
8467       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8468     }
8469
8470     if (!MovDelay[x][y])
8471     {
8472       Feld[x][y] = EL_EMPTY;
8473       TEST_DrawLevelField(x, y);
8474
8475       /* don't let mole enter this field in this cycle;
8476          (give priority to objects falling to this field from above) */
8477       Stop[x][y] = TRUE;
8478     }
8479   }
8480 }
8481
8482 void AmoebeAbleger(int ax, int ay)
8483 {
8484   int i;
8485   int element = Feld[ax][ay];
8486   int graphic = el2img(element);
8487   int newax = ax, neway = ay;
8488   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8489   static int xy[4][2] =
8490   {
8491     { 0, -1 },
8492     { -1, 0 },
8493     { +1, 0 },
8494     { 0, +1 }
8495   };
8496
8497   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8498   {
8499     Feld[ax][ay] = EL_AMOEBA_DEAD;
8500     TEST_DrawLevelField(ax, ay);
8501     return;
8502   }
8503
8504   if (IS_ANIMATED(graphic))
8505     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8506
8507   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8508     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8509
8510   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8511   {
8512     MovDelay[ax][ay]--;
8513     if (MovDelay[ax][ay])
8514       return;
8515   }
8516
8517   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8518   {
8519     int start = RND(4);
8520     int x = ax + xy[start][0];
8521     int y = ay + xy[start][1];
8522
8523     if (!IN_LEV_FIELD(x, y))
8524       return;
8525
8526     if (IS_FREE(x, y) ||
8527         CAN_GROW_INTO(Feld[x][y]) ||
8528         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8529         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8530     {
8531       newax = x;
8532       neway = y;
8533     }
8534
8535     if (newax == ax && neway == ay)
8536       return;
8537   }
8538   else                          /* normal or "filled" (BD style) amoeba */
8539   {
8540     int start = RND(4);
8541     boolean waiting_for_player = FALSE;
8542
8543     for (i = 0; i < NUM_DIRECTIONS; i++)
8544     {
8545       int j = (start + i) % 4;
8546       int x = ax + xy[j][0];
8547       int y = ay + xy[j][1];
8548
8549       if (!IN_LEV_FIELD(x, y))
8550         continue;
8551
8552       if (IS_FREE(x, y) ||
8553           CAN_GROW_INTO(Feld[x][y]) ||
8554           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8555           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8556       {
8557         newax = x;
8558         neway = y;
8559         break;
8560       }
8561       else if (IS_PLAYER(x, y))
8562         waiting_for_player = TRUE;
8563     }
8564
8565     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8566     {
8567       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8568       {
8569         Feld[ax][ay] = EL_AMOEBA_DEAD;
8570         TEST_DrawLevelField(ax, ay);
8571         AmoebaCnt[AmoebaNr[ax][ay]]--;
8572
8573         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8574         {
8575           if (element == EL_AMOEBA_FULL)
8576             AmoebeUmwandeln(ax, ay);
8577           else if (element == EL_BD_AMOEBA)
8578             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8579         }
8580       }
8581       return;
8582     }
8583     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8584     {
8585       /* amoeba gets larger by growing in some direction */
8586
8587       int new_group_nr = AmoebaNr[ax][ay];
8588
8589 #ifdef DEBUG
8590   if (new_group_nr == 0)
8591   {
8592     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8593     printf("AmoebeAbleger(): This should never happen!\n");
8594     return;
8595   }
8596 #endif
8597
8598       AmoebaNr[newax][neway] = new_group_nr;
8599       AmoebaCnt[new_group_nr]++;
8600       AmoebaCnt2[new_group_nr]++;
8601
8602       /* if amoeba touches other amoeba(s) after growing, unify them */
8603       AmoebenVereinigen(newax, neway);
8604
8605       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8606       {
8607         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8608         return;
8609       }
8610     }
8611   }
8612
8613   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8614       (neway == lev_fieldy - 1 && newax != ax))
8615   {
8616     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8617     Store[newax][neway] = element;
8618   }
8619   else if (neway == ay || element == EL_EMC_DRIPPER)
8620   {
8621     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8622
8623     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8624   }
8625   else
8626   {
8627     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8628     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8629     Store[ax][ay] = EL_AMOEBA_DROP;
8630     ContinueMoving(ax, ay);
8631     return;
8632   }
8633
8634   TEST_DrawLevelField(newax, neway);
8635 }
8636
8637 void Life(int ax, int ay)
8638 {
8639   int x1, y1, x2, y2;
8640   int life_time = 40;
8641   int element = Feld[ax][ay];
8642   int graphic = el2img(element);
8643   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8644                          level.biomaze);
8645   boolean changed = FALSE;
8646
8647   if (IS_ANIMATED(graphic))
8648     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8649
8650   if (Stop[ax][ay])
8651     return;
8652
8653   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8654     MovDelay[ax][ay] = life_time;
8655
8656   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8657   {
8658     MovDelay[ax][ay]--;
8659     if (MovDelay[ax][ay])
8660       return;
8661   }
8662
8663   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8664   {
8665     int xx = ax+x1, yy = ay+y1;
8666     int nachbarn = 0;
8667
8668     if (!IN_LEV_FIELD(xx, yy))
8669       continue;
8670
8671     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8672     {
8673       int x = xx+x2, y = yy+y2;
8674
8675       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8676         continue;
8677
8678       if (((Feld[x][y] == element ||
8679             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8680            !Stop[x][y]) ||
8681           (IS_FREE(x, y) && Stop[x][y]))
8682         nachbarn++;
8683     }
8684
8685     if (xx == ax && yy == ay)           /* field in the middle */
8686     {
8687       if (nachbarn < life_parameter[0] ||
8688           nachbarn > life_parameter[1])
8689       {
8690         Feld[xx][yy] = EL_EMPTY;
8691         if (!Stop[xx][yy])
8692           TEST_DrawLevelField(xx, yy);
8693         Stop[xx][yy] = TRUE;
8694         changed = TRUE;
8695       }
8696     }
8697     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8698     {                                   /* free border field */
8699       if (nachbarn >= life_parameter[2] &&
8700           nachbarn <= life_parameter[3])
8701       {
8702         Feld[xx][yy] = element;
8703         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8704         if (!Stop[xx][yy])
8705           TEST_DrawLevelField(xx, yy);
8706         Stop[xx][yy] = TRUE;
8707         changed = TRUE;
8708       }
8709     }
8710   }
8711
8712   if (changed)
8713     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8714                    SND_GAME_OF_LIFE_GROWING);
8715 }
8716
8717 static void InitRobotWheel(int x, int y)
8718 {
8719   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8720 }
8721
8722 static void RunRobotWheel(int x, int y)
8723 {
8724   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8725 }
8726
8727 static void StopRobotWheel(int x, int y)
8728 {
8729   if (ZX == x && ZY == y)
8730   {
8731     ZX = ZY = -1;
8732
8733     game.robot_wheel_active = FALSE;
8734   }
8735 }
8736
8737 static void InitTimegateWheel(int x, int y)
8738 {
8739   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8740 }
8741
8742 static void RunTimegateWheel(int x, int y)
8743 {
8744   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8745 }
8746
8747 static void InitMagicBallDelay(int x, int y)
8748 {
8749   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8750 }
8751
8752 static void ActivateMagicBall(int bx, int by)
8753 {
8754   int x, y;
8755
8756   if (level.ball_random)
8757   {
8758     int pos_border = RND(8);    /* select one of the eight border elements */
8759     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8760     int xx = pos_content % 3;
8761     int yy = pos_content / 3;
8762
8763     x = bx - 1 + xx;
8764     y = by - 1 + yy;
8765
8766     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8767       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8768   }
8769   else
8770   {
8771     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8772     {
8773       int xx = x - bx + 1;
8774       int yy = y - by + 1;
8775
8776       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8777         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8778     }
8779   }
8780
8781   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8782 }
8783
8784 void CheckExit(int x, int y)
8785 {
8786   if (local_player->gems_still_needed > 0 ||
8787       local_player->sokobanfields_still_needed > 0 ||
8788       local_player->lights_still_needed > 0)
8789   {
8790     int element = Feld[x][y];
8791     int graphic = el2img(element);
8792
8793     if (IS_ANIMATED(graphic))
8794       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8795
8796     return;
8797   }
8798
8799   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8800     return;
8801
8802   Feld[x][y] = EL_EXIT_OPENING;
8803
8804   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8805 }
8806
8807 void CheckExitEM(int x, int y)
8808 {
8809   if (local_player->gems_still_needed > 0 ||
8810       local_player->sokobanfields_still_needed > 0 ||
8811       local_player->lights_still_needed > 0)
8812   {
8813     int element = Feld[x][y];
8814     int graphic = el2img(element);
8815
8816     if (IS_ANIMATED(graphic))
8817       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8818
8819     return;
8820   }
8821
8822   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8823     return;
8824
8825   Feld[x][y] = EL_EM_EXIT_OPENING;
8826
8827   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8828 }
8829
8830 void CheckExitSteel(int x, int y)
8831 {
8832   if (local_player->gems_still_needed > 0 ||
8833       local_player->sokobanfields_still_needed > 0 ||
8834       local_player->lights_still_needed > 0)
8835   {
8836     int element = Feld[x][y];
8837     int graphic = el2img(element);
8838
8839     if (IS_ANIMATED(graphic))
8840       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8841
8842     return;
8843   }
8844
8845   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8846     return;
8847
8848   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8849
8850   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8851 }
8852
8853 void CheckExitSteelEM(int x, int y)
8854 {
8855   if (local_player->gems_still_needed > 0 ||
8856       local_player->sokobanfields_still_needed > 0 ||
8857       local_player->lights_still_needed > 0)
8858   {
8859     int element = Feld[x][y];
8860     int graphic = el2img(element);
8861
8862     if (IS_ANIMATED(graphic))
8863       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8864
8865     return;
8866   }
8867
8868   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8869     return;
8870
8871   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8872
8873   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8874 }
8875
8876 void CheckExitSP(int x, int y)
8877 {
8878   if (local_player->gems_still_needed > 0)
8879   {
8880     int element = Feld[x][y];
8881     int graphic = el2img(element);
8882
8883     if (IS_ANIMATED(graphic))
8884       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8885
8886     return;
8887   }
8888
8889   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8890     return;
8891
8892   Feld[x][y] = EL_SP_EXIT_OPENING;
8893
8894   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8895 }
8896
8897 static void CloseAllOpenTimegates()
8898 {
8899   int x, y;
8900
8901   SCAN_PLAYFIELD(x, y)
8902   {
8903     int element = Feld[x][y];
8904
8905     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8906     {
8907       Feld[x][y] = EL_TIMEGATE_CLOSING;
8908
8909       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8910     }
8911   }
8912 }
8913
8914 void DrawTwinkleOnField(int x, int y)
8915 {
8916   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8917     return;
8918
8919   if (Feld[x][y] == EL_BD_DIAMOND)
8920     return;
8921
8922   if (MovDelay[x][y] == 0)      /* next animation frame */
8923     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8924
8925   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8926   {
8927     MovDelay[x][y]--;
8928
8929     DrawLevelElementAnimation(x, y, Feld[x][y]);
8930
8931     if (MovDelay[x][y] != 0)
8932     {
8933       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8934                                            10 - MovDelay[x][y]);
8935
8936       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8937     }
8938   }
8939 }
8940
8941 void MauerWaechst(int x, int y)
8942 {
8943   int delay = 6;
8944
8945   if (!MovDelay[x][y])          /* next animation frame */
8946     MovDelay[x][y] = 3 * delay;
8947
8948   if (MovDelay[x][y])           /* wait some time before next frame */
8949   {
8950     MovDelay[x][y]--;
8951
8952     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8953     {
8954       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8955       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8956
8957       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8958     }
8959
8960     if (!MovDelay[x][y])
8961     {
8962       if (MovDir[x][y] == MV_LEFT)
8963       {
8964         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8965           TEST_DrawLevelField(x - 1, y);
8966       }
8967       else if (MovDir[x][y] == MV_RIGHT)
8968       {
8969         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8970           TEST_DrawLevelField(x + 1, y);
8971       }
8972       else if (MovDir[x][y] == MV_UP)
8973       {
8974         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8975           TEST_DrawLevelField(x, y - 1);
8976       }
8977       else
8978       {
8979         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8980           TEST_DrawLevelField(x, y + 1);
8981       }
8982
8983       Feld[x][y] = Store[x][y];
8984       Store[x][y] = 0;
8985       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8986       TEST_DrawLevelField(x, y);
8987     }
8988   }
8989 }
8990
8991 void MauerAbleger(int ax, int ay)
8992 {
8993   int element = Feld[ax][ay];
8994   int graphic = el2img(element);
8995   boolean oben_frei = FALSE, unten_frei = FALSE;
8996   boolean links_frei = FALSE, rechts_frei = FALSE;
8997   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8998   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8999   boolean new_wall = FALSE;
9000
9001   if (IS_ANIMATED(graphic))
9002     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9003
9004   if (!MovDelay[ax][ay])        /* start building new wall */
9005     MovDelay[ax][ay] = 6;
9006
9007   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9008   {
9009     MovDelay[ax][ay]--;
9010     if (MovDelay[ax][ay])
9011       return;
9012   }
9013
9014   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9015     oben_frei = TRUE;
9016   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9017     unten_frei = TRUE;
9018   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9019     links_frei = TRUE;
9020   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9021     rechts_frei = TRUE;
9022
9023   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9024       element == EL_EXPANDABLE_WALL_ANY)
9025   {
9026     if (oben_frei)
9027     {
9028       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9029       Store[ax][ay-1] = element;
9030       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9031       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9032         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9033                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9034       new_wall = TRUE;
9035     }
9036     if (unten_frei)
9037     {
9038       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9039       Store[ax][ay+1] = element;
9040       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9041       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9042         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9043                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9044       new_wall = TRUE;
9045     }
9046   }
9047
9048   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9049       element == EL_EXPANDABLE_WALL_ANY ||
9050       element == EL_EXPANDABLE_WALL ||
9051       element == EL_BD_EXPANDABLE_WALL)
9052   {
9053     if (links_frei)
9054     {
9055       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9056       Store[ax-1][ay] = element;
9057       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9058       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9059         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9060                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9061       new_wall = TRUE;
9062     }
9063
9064     if (rechts_frei)
9065     {
9066       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9067       Store[ax+1][ay] = element;
9068       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9069       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9070         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9071                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9072       new_wall = TRUE;
9073     }
9074   }
9075
9076   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9077     TEST_DrawLevelField(ax, ay);
9078
9079   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9080     oben_massiv = TRUE;
9081   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9082     unten_massiv = TRUE;
9083   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9084     links_massiv = TRUE;
9085   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9086     rechts_massiv = TRUE;
9087
9088   if (((oben_massiv && unten_massiv) ||
9089        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9090        element == EL_EXPANDABLE_WALL) &&
9091       ((links_massiv && rechts_massiv) ||
9092        element == EL_EXPANDABLE_WALL_VERTICAL))
9093     Feld[ax][ay] = EL_WALL;
9094
9095   if (new_wall)
9096     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9097 }
9098
9099 void MauerAblegerStahl(int ax, int ay)
9100 {
9101   int element = Feld[ax][ay];
9102   int graphic = el2img(element);
9103   boolean oben_frei = FALSE, unten_frei = FALSE;
9104   boolean links_frei = FALSE, rechts_frei = FALSE;
9105   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9106   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9107   boolean new_wall = FALSE;
9108
9109   if (IS_ANIMATED(graphic))
9110     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9111
9112   if (!MovDelay[ax][ay])        /* start building new wall */
9113     MovDelay[ax][ay] = 6;
9114
9115   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9116   {
9117     MovDelay[ax][ay]--;
9118     if (MovDelay[ax][ay])
9119       return;
9120   }
9121
9122   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9123     oben_frei = TRUE;
9124   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9125     unten_frei = TRUE;
9126   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9127     links_frei = TRUE;
9128   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9129     rechts_frei = TRUE;
9130
9131   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9132       element == EL_EXPANDABLE_STEELWALL_ANY)
9133   {
9134     if (oben_frei)
9135     {
9136       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9137       Store[ax][ay-1] = element;
9138       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9139       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9140         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9141                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9142       new_wall = TRUE;
9143     }
9144     if (unten_frei)
9145     {
9146       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9147       Store[ax][ay+1] = element;
9148       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9149       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9150         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9151                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9152       new_wall = TRUE;
9153     }
9154   }
9155
9156   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9157       element == EL_EXPANDABLE_STEELWALL_ANY)
9158   {
9159     if (links_frei)
9160     {
9161       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9162       Store[ax-1][ay] = element;
9163       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9164       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9165         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9166                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9167       new_wall = TRUE;
9168     }
9169
9170     if (rechts_frei)
9171     {
9172       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9173       Store[ax+1][ay] = element;
9174       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9175       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9176         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9177                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9178       new_wall = TRUE;
9179     }
9180   }
9181
9182   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9183     oben_massiv = TRUE;
9184   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9185     unten_massiv = TRUE;
9186   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9187     links_massiv = TRUE;
9188   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9189     rechts_massiv = TRUE;
9190
9191   if (((oben_massiv && unten_massiv) ||
9192        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9193       ((links_massiv && rechts_massiv) ||
9194        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9195     Feld[ax][ay] = EL_STEELWALL;
9196
9197   if (new_wall)
9198     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9199 }
9200
9201 void CheckForDragon(int x, int y)
9202 {
9203   int i, j;
9204   boolean dragon_found = FALSE;
9205   static int xy[4][2] =
9206   {
9207     { 0, -1 },
9208     { -1, 0 },
9209     { +1, 0 },
9210     { 0, +1 }
9211   };
9212
9213   for (i = 0; i < NUM_DIRECTIONS; i++)
9214   {
9215     for (j = 0; j < 4; j++)
9216     {
9217       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9218
9219       if (IN_LEV_FIELD(xx, yy) &&
9220           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9221       {
9222         if (Feld[xx][yy] == EL_DRAGON)
9223           dragon_found = TRUE;
9224       }
9225       else
9226         break;
9227     }
9228   }
9229
9230   if (!dragon_found)
9231   {
9232     for (i = 0; i < NUM_DIRECTIONS; i++)
9233     {
9234       for (j = 0; j < 3; j++)
9235       {
9236         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9237   
9238         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9239         {
9240           Feld[xx][yy] = EL_EMPTY;
9241           TEST_DrawLevelField(xx, yy);
9242         }
9243         else
9244           break;
9245       }
9246     }
9247   }
9248 }
9249
9250 static void InitBuggyBase(int x, int y)
9251 {
9252   int element = Feld[x][y];
9253   int activating_delay = FRAMES_PER_SECOND / 4;
9254
9255   ChangeDelay[x][y] =
9256     (element == EL_SP_BUGGY_BASE ?
9257      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9258      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9259      activating_delay :
9260      element == EL_SP_BUGGY_BASE_ACTIVE ?
9261      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9262 }
9263
9264 static void WarnBuggyBase(int x, int y)
9265 {
9266   int i;
9267   static int xy[4][2] =
9268   {
9269     { 0, -1 },
9270     { -1, 0 },
9271     { +1, 0 },
9272     { 0, +1 }
9273   };
9274
9275   for (i = 0; i < NUM_DIRECTIONS; i++)
9276   {
9277     int xx = x + xy[i][0];
9278     int yy = y + xy[i][1];
9279
9280     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9281     {
9282       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9283
9284       break;
9285     }
9286   }
9287 }
9288
9289 static void InitTrap(int x, int y)
9290 {
9291   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9292 }
9293
9294 static void ActivateTrap(int x, int y)
9295 {
9296   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9297 }
9298
9299 static void ChangeActiveTrap(int x, int y)
9300 {
9301   int graphic = IMG_TRAP_ACTIVE;
9302
9303   /* if new animation frame was drawn, correct crumbled sand border */
9304   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9305     TEST_DrawLevelFieldCrumbled(x, y);
9306 }
9307
9308 static int getSpecialActionElement(int element, int number, int base_element)
9309 {
9310   return (element != EL_EMPTY ? element :
9311           number != -1 ? base_element + number - 1 :
9312           EL_EMPTY);
9313 }
9314
9315 static int getModifiedActionNumber(int value_old, int operator, int operand,
9316                                    int value_min, int value_max)
9317 {
9318   int value_new = (operator == CA_MODE_SET      ? operand :
9319                    operator == CA_MODE_ADD      ? value_old + operand :
9320                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9321                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9322                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9323                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9324                    value_old);
9325
9326   return (value_new < value_min ? value_min :
9327           value_new > value_max ? value_max :
9328           value_new);
9329 }
9330
9331 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9332 {
9333   struct ElementInfo *ei = &element_info[element];
9334   struct ElementChangeInfo *change = &ei->change_page[page];
9335   int target_element = change->target_element;
9336   int action_type = change->action_type;
9337   int action_mode = change->action_mode;
9338   int action_arg = change->action_arg;
9339   int action_element = change->action_element;
9340   int i;
9341
9342   if (!change->has_action)
9343     return;
9344
9345   /* ---------- determine action paramater values -------------------------- */
9346
9347   int level_time_value =
9348     (level.time > 0 ? TimeLeft :
9349      TimePlayed);
9350
9351   int action_arg_element_raw =
9352     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9353      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9354      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9355      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9356      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9357      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9358      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9359      EL_EMPTY);
9360   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9361
9362   int action_arg_direction =
9363     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9364      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9365      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9366      change->actual_trigger_side :
9367      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9368      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9369      MV_NONE);
9370
9371   int action_arg_number_min =
9372     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9373      CA_ARG_MIN);
9374
9375   int action_arg_number_max =
9376     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9377      action_type == CA_SET_LEVEL_GEMS ? 999 :
9378      action_type == CA_SET_LEVEL_TIME ? 9999 :
9379      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9380      action_type == CA_SET_CE_VALUE ? 9999 :
9381      action_type == CA_SET_CE_SCORE ? 9999 :
9382      CA_ARG_MAX);
9383
9384   int action_arg_number_reset =
9385     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9386      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9387      action_type == CA_SET_LEVEL_TIME ? level.time :
9388      action_type == CA_SET_LEVEL_SCORE ? 0 :
9389      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9390      action_type == CA_SET_CE_SCORE ? 0 :
9391      0);
9392
9393   int action_arg_number =
9394     (action_arg <= CA_ARG_MAX ? action_arg :
9395      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9396      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9397      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9398      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9399      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9400      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9401      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9402      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9403      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9404      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9405      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9406      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9407      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9408      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9409      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9410      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9411      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9412      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9413      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9414      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9415      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9416      -1);
9417
9418   int action_arg_number_old =
9419     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9420      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9421      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9422      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9423      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9424      0);
9425
9426   int action_arg_number_new =
9427     getModifiedActionNumber(action_arg_number_old,
9428                             action_mode, action_arg_number,
9429                             action_arg_number_min, action_arg_number_max);
9430
9431   int trigger_player_bits =
9432     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9433      change->actual_trigger_player_bits : change->trigger_player);
9434
9435   int action_arg_player_bits =
9436     (action_arg >= CA_ARG_PLAYER_1 &&
9437      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9438      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9439      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9440      PLAYER_BITS_ANY);
9441
9442   /* ---------- execute action  -------------------------------------------- */
9443
9444   switch (action_type)
9445   {
9446     case CA_NO_ACTION:
9447     {
9448       return;
9449     }
9450
9451     /* ---------- level actions  ------------------------------------------- */
9452
9453     case CA_RESTART_LEVEL:
9454     {
9455       game.restart_level = TRUE;
9456
9457       break;
9458     }
9459
9460     case CA_SHOW_ENVELOPE:
9461     {
9462       int element = getSpecialActionElement(action_arg_element,
9463                                             action_arg_number, EL_ENVELOPE_1);
9464
9465       if (IS_ENVELOPE(element))
9466         local_player->show_envelope = element;
9467
9468       break;
9469     }
9470
9471     case CA_SET_LEVEL_TIME:
9472     {
9473       if (level.time > 0)       /* only modify limited time value */
9474       {
9475         TimeLeft = action_arg_number_new;
9476
9477         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9478
9479         DisplayGameControlValues();
9480
9481         if (!TimeLeft && setup.time_limit)
9482           for (i = 0; i < MAX_PLAYERS; i++)
9483             KillPlayer(&stored_player[i]);
9484       }
9485
9486       break;
9487     }
9488
9489     case CA_SET_LEVEL_SCORE:
9490     {
9491       local_player->score = action_arg_number_new;
9492
9493       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9494
9495       DisplayGameControlValues();
9496
9497       break;
9498     }
9499
9500     case CA_SET_LEVEL_GEMS:
9501     {
9502       local_player->gems_still_needed = action_arg_number_new;
9503
9504       game.snapshot.collected_item = TRUE;
9505
9506       game_panel_controls[GAME_PANEL_GEMS].value =
9507         local_player->gems_still_needed;
9508
9509       DisplayGameControlValues();
9510
9511       break;
9512     }
9513
9514     case CA_SET_LEVEL_WIND:
9515     {
9516       game.wind_direction = action_arg_direction;
9517
9518       break;
9519     }
9520
9521     case CA_SET_LEVEL_RANDOM_SEED:
9522     {
9523       /* ensure that setting a new random seed while playing is predictable */
9524       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9525
9526       break;
9527     }
9528
9529     /* ---------- player actions  ------------------------------------------ */
9530
9531     case CA_MOVE_PLAYER:
9532     {
9533       /* automatically move to the next field in specified direction */
9534       for (i = 0; i < MAX_PLAYERS; i++)
9535         if (trigger_player_bits & (1 << i))
9536           stored_player[i].programmed_action = action_arg_direction;
9537
9538       break;
9539     }
9540
9541     case CA_EXIT_PLAYER:
9542     {
9543       for (i = 0; i < MAX_PLAYERS; i++)
9544         if (action_arg_player_bits & (1 << i))
9545           PlayerWins(&stored_player[i]);
9546
9547       break;
9548     }
9549
9550     case CA_KILL_PLAYER:
9551     {
9552       for (i = 0; i < MAX_PLAYERS; i++)
9553         if (action_arg_player_bits & (1 << i))
9554           KillPlayer(&stored_player[i]);
9555
9556       break;
9557     }
9558
9559     case CA_SET_PLAYER_KEYS:
9560     {
9561       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9562       int element = getSpecialActionElement(action_arg_element,
9563                                             action_arg_number, EL_KEY_1);
9564
9565       if (IS_KEY(element))
9566       {
9567         for (i = 0; i < MAX_PLAYERS; i++)
9568         {
9569           if (trigger_player_bits & (1 << i))
9570           {
9571             stored_player[i].key[KEY_NR(element)] = key_state;
9572
9573             DrawGameDoorValues();
9574           }
9575         }
9576       }
9577
9578       break;
9579     }
9580
9581     case CA_SET_PLAYER_SPEED:
9582     {
9583       for (i = 0; i < MAX_PLAYERS; i++)
9584       {
9585         if (trigger_player_bits & (1 << i))
9586         {
9587           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9588
9589           if (action_arg == CA_ARG_SPEED_FASTER &&
9590               stored_player[i].cannot_move)
9591           {
9592             action_arg_number = STEPSIZE_VERY_SLOW;
9593           }
9594           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9595                    action_arg == CA_ARG_SPEED_FASTER)
9596           {
9597             action_arg_number = 2;
9598             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9599                            CA_MODE_MULTIPLY);
9600           }
9601           else if (action_arg == CA_ARG_NUMBER_RESET)
9602           {
9603             action_arg_number = level.initial_player_stepsize[i];
9604           }
9605
9606           move_stepsize =
9607             getModifiedActionNumber(move_stepsize,
9608                                     action_mode,
9609                                     action_arg_number,
9610                                     action_arg_number_min,
9611                                     action_arg_number_max);
9612
9613           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9614         }
9615       }
9616
9617       break;
9618     }
9619
9620     case CA_SET_PLAYER_SHIELD:
9621     {
9622       for (i = 0; i < MAX_PLAYERS; i++)
9623       {
9624         if (trigger_player_bits & (1 << i))
9625         {
9626           if (action_arg == CA_ARG_SHIELD_OFF)
9627           {
9628             stored_player[i].shield_normal_time_left = 0;
9629             stored_player[i].shield_deadly_time_left = 0;
9630           }
9631           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9632           {
9633             stored_player[i].shield_normal_time_left = 999999;
9634           }
9635           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9636           {
9637             stored_player[i].shield_normal_time_left = 999999;
9638             stored_player[i].shield_deadly_time_left = 999999;
9639           }
9640         }
9641       }
9642
9643       break;
9644     }
9645
9646     case CA_SET_PLAYER_GRAVITY:
9647     {
9648       for (i = 0; i < MAX_PLAYERS; i++)
9649       {
9650         if (trigger_player_bits & (1 << i))
9651         {
9652           stored_player[i].gravity =
9653             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9654              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9655              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9656              stored_player[i].gravity);
9657         }
9658       }
9659
9660       break;
9661     }
9662
9663     case CA_SET_PLAYER_ARTWORK:
9664     {
9665       for (i = 0; i < MAX_PLAYERS; i++)
9666       {
9667         if (trigger_player_bits & (1 << i))
9668         {
9669           int artwork_element = action_arg_element;
9670
9671           if (action_arg == CA_ARG_ELEMENT_RESET)
9672             artwork_element =
9673               (level.use_artwork_element[i] ? level.artwork_element[i] :
9674                stored_player[i].element_nr);
9675
9676           if (stored_player[i].artwork_element != artwork_element)
9677             stored_player[i].Frame = 0;
9678
9679           stored_player[i].artwork_element = artwork_element;
9680
9681           SetPlayerWaiting(&stored_player[i], FALSE);
9682
9683           /* set number of special actions for bored and sleeping animation */
9684           stored_player[i].num_special_action_bored =
9685             get_num_special_action(artwork_element,
9686                                    ACTION_BORING_1, ACTION_BORING_LAST);
9687           stored_player[i].num_special_action_sleeping =
9688             get_num_special_action(artwork_element,
9689                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9690         }
9691       }
9692
9693       break;
9694     }
9695
9696     case CA_SET_PLAYER_INVENTORY:
9697     {
9698       for (i = 0; i < MAX_PLAYERS; i++)
9699       {
9700         struct PlayerInfo *player = &stored_player[i];
9701         int j, k;
9702
9703         if (trigger_player_bits & (1 << i))
9704         {
9705           int inventory_element = action_arg_element;
9706
9707           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9708               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9709               action_arg == CA_ARG_ELEMENT_ACTION)
9710           {
9711             int element = inventory_element;
9712             int collect_count = element_info[element].collect_count_initial;
9713
9714             if (!IS_CUSTOM_ELEMENT(element))
9715               collect_count = 1;
9716
9717             if (collect_count == 0)
9718               player->inventory_infinite_element = element;
9719             else
9720               for (k = 0; k < collect_count; k++)
9721                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9722                   player->inventory_element[player->inventory_size++] =
9723                     element;
9724           }
9725           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9726                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9727                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9728           {
9729             if (player->inventory_infinite_element != EL_UNDEFINED &&
9730                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9731                                      action_arg_element_raw))
9732               player->inventory_infinite_element = EL_UNDEFINED;
9733
9734             for (k = 0, j = 0; j < player->inventory_size; j++)
9735             {
9736               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9737                                         action_arg_element_raw))
9738                 player->inventory_element[k++] = player->inventory_element[j];
9739             }
9740
9741             player->inventory_size = k;
9742           }
9743           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9744           {
9745             if (player->inventory_size > 0)
9746             {
9747               for (j = 0; j < player->inventory_size - 1; j++)
9748                 player->inventory_element[j] = player->inventory_element[j + 1];
9749
9750               player->inventory_size--;
9751             }
9752           }
9753           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9754           {
9755             if (player->inventory_size > 0)
9756               player->inventory_size--;
9757           }
9758           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9759           {
9760             player->inventory_infinite_element = EL_UNDEFINED;
9761             player->inventory_size = 0;
9762           }
9763           else if (action_arg == CA_ARG_INVENTORY_RESET)
9764           {
9765             player->inventory_infinite_element = EL_UNDEFINED;
9766             player->inventory_size = 0;
9767
9768             if (level.use_initial_inventory[i])
9769             {
9770               for (j = 0; j < level.initial_inventory_size[i]; j++)
9771               {
9772                 int element = level.initial_inventory_content[i][j];
9773                 int collect_count = element_info[element].collect_count_initial;
9774
9775                 if (!IS_CUSTOM_ELEMENT(element))
9776                   collect_count = 1;
9777
9778                 if (collect_count == 0)
9779                   player->inventory_infinite_element = element;
9780                 else
9781                   for (k = 0; k < collect_count; k++)
9782                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9783                       player->inventory_element[player->inventory_size++] =
9784                         element;
9785               }
9786             }
9787           }
9788         }
9789       }
9790
9791       break;
9792     }
9793
9794     /* ---------- CE actions  ---------------------------------------------- */
9795
9796     case CA_SET_CE_VALUE:
9797     {
9798       int last_ce_value = CustomValue[x][y];
9799
9800       CustomValue[x][y] = action_arg_number_new;
9801
9802       if (CustomValue[x][y] != last_ce_value)
9803       {
9804         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9805         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9806
9807         if (CustomValue[x][y] == 0)
9808         {
9809           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9810           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9811         }
9812       }
9813
9814       break;
9815     }
9816
9817     case CA_SET_CE_SCORE:
9818     {
9819       int last_ce_score = ei->collect_score;
9820
9821       ei->collect_score = action_arg_number_new;
9822
9823       if (ei->collect_score != last_ce_score)
9824       {
9825         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9826         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9827
9828         if (ei->collect_score == 0)
9829         {
9830           int xx, yy;
9831
9832           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9833           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9834
9835           /*
9836             This is a very special case that seems to be a mixture between
9837             CheckElementChange() and CheckTriggeredElementChange(): while
9838             the first one only affects single elements that are triggered
9839             directly, the second one affects multiple elements in the playfield
9840             that are triggered indirectly by another element. This is a third
9841             case: Changing the CE score always affects multiple identical CEs,
9842             so every affected CE must be checked, not only the single CE for
9843             which the CE score was changed in the first place (as every instance
9844             of that CE shares the same CE score, and therefore also can change)!
9845           */
9846           SCAN_PLAYFIELD(xx, yy)
9847           {
9848             if (Feld[xx][yy] == element)
9849               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9850                                  CE_SCORE_GETS_ZERO);
9851           }
9852         }
9853       }
9854
9855       break;
9856     }
9857
9858     case CA_SET_CE_ARTWORK:
9859     {
9860       int artwork_element = action_arg_element;
9861       boolean reset_frame = FALSE;
9862       int xx, yy;
9863
9864       if (action_arg == CA_ARG_ELEMENT_RESET)
9865         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9866                            element);
9867
9868       if (ei->gfx_element != artwork_element)
9869         reset_frame = TRUE;
9870
9871       ei->gfx_element = artwork_element;
9872
9873       SCAN_PLAYFIELD(xx, yy)
9874       {
9875         if (Feld[xx][yy] == element)
9876         {
9877           if (reset_frame)
9878           {
9879             ResetGfxAnimation(xx, yy);
9880             ResetRandomAnimationValue(xx, yy);
9881           }
9882
9883           TEST_DrawLevelField(xx, yy);
9884         }
9885       }
9886
9887       break;
9888     }
9889
9890     /* ---------- engine actions  ------------------------------------------ */
9891
9892     case CA_SET_ENGINE_SCAN_MODE:
9893     {
9894       InitPlayfieldScanMode(action_arg);
9895
9896       break;
9897     }
9898
9899     default:
9900       break;
9901   }
9902 }
9903
9904 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9905 {
9906   int old_element = Feld[x][y];
9907   int new_element = GetElementFromGroupElement(element);
9908   int previous_move_direction = MovDir[x][y];
9909   int last_ce_value = CustomValue[x][y];
9910   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9911   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9912   boolean add_player_onto_element = (new_element_is_player &&
9913                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9914                                      IS_WALKABLE(old_element));
9915
9916   if (!add_player_onto_element)
9917   {
9918     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9919       RemoveMovingField(x, y);
9920     else
9921       RemoveField(x, y);
9922
9923     Feld[x][y] = new_element;
9924
9925     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9926       MovDir[x][y] = previous_move_direction;
9927
9928     if (element_info[new_element].use_last_ce_value)
9929       CustomValue[x][y] = last_ce_value;
9930
9931     InitField_WithBug1(x, y, FALSE);
9932
9933     new_element = Feld[x][y];   /* element may have changed */
9934
9935     ResetGfxAnimation(x, y);
9936     ResetRandomAnimationValue(x, y);
9937
9938     TEST_DrawLevelField(x, y);
9939
9940     if (GFX_CRUMBLED(new_element))
9941       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9942   }
9943
9944   /* check if element under the player changes from accessible to unaccessible
9945      (needed for special case of dropping element which then changes) */
9946   /* (must be checked after creating new element for walkable group elements) */
9947   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9948       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9949   {
9950     Bang(x, y);
9951
9952     return;
9953   }
9954
9955   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9956   if (new_element_is_player)
9957     RelocatePlayer(x, y, new_element);
9958
9959   if (is_change)
9960     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9961
9962   TestIfBadThingTouchesPlayer(x, y);
9963   TestIfPlayerTouchesCustomElement(x, y);
9964   TestIfElementTouchesCustomElement(x, y);
9965 }
9966
9967 static void CreateField(int x, int y, int element)
9968 {
9969   CreateFieldExt(x, y, element, FALSE);
9970 }
9971
9972 static void CreateElementFromChange(int x, int y, int element)
9973 {
9974   element = GET_VALID_RUNTIME_ELEMENT(element);
9975
9976   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9977   {
9978     int old_element = Feld[x][y];
9979
9980     /* prevent changed element from moving in same engine frame
9981        unless both old and new element can either fall or move */
9982     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9983         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9984       Stop[x][y] = TRUE;
9985   }
9986
9987   CreateFieldExt(x, y, element, TRUE);
9988 }
9989
9990 static boolean ChangeElement(int x, int y, int element, int page)
9991 {
9992   struct ElementInfo *ei = &element_info[element];
9993   struct ElementChangeInfo *change = &ei->change_page[page];
9994   int ce_value = CustomValue[x][y];
9995   int ce_score = ei->collect_score;
9996   int target_element;
9997   int old_element = Feld[x][y];
9998
9999   /* always use default change event to prevent running into a loop */
10000   if (ChangeEvent[x][y] == -1)
10001     ChangeEvent[x][y] = CE_DELAY;
10002
10003   if (ChangeEvent[x][y] == CE_DELAY)
10004   {
10005     /* reset actual trigger element, trigger player and action element */
10006     change->actual_trigger_element = EL_EMPTY;
10007     change->actual_trigger_player = EL_EMPTY;
10008     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10009     change->actual_trigger_side = CH_SIDE_NONE;
10010     change->actual_trigger_ce_value = 0;
10011     change->actual_trigger_ce_score = 0;
10012   }
10013
10014   /* do not change elements more than a specified maximum number of changes */
10015   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10016     return FALSE;
10017
10018   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10019
10020   if (change->explode)
10021   {
10022     Bang(x, y);
10023
10024     return TRUE;
10025   }
10026
10027   if (change->use_target_content)
10028   {
10029     boolean complete_replace = TRUE;
10030     boolean can_replace[3][3];
10031     int xx, yy;
10032
10033     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10034     {
10035       boolean is_empty;
10036       boolean is_walkable;
10037       boolean is_diggable;
10038       boolean is_collectible;
10039       boolean is_removable;
10040       boolean is_destructible;
10041       int ex = x + xx - 1;
10042       int ey = y + yy - 1;
10043       int content_element = change->target_content.e[xx][yy];
10044       int e;
10045
10046       can_replace[xx][yy] = TRUE;
10047
10048       if (ex == x && ey == y)   /* do not check changing element itself */
10049         continue;
10050
10051       if (content_element == EL_EMPTY_SPACE)
10052       {
10053         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10054
10055         continue;
10056       }
10057
10058       if (!IN_LEV_FIELD(ex, ey))
10059       {
10060         can_replace[xx][yy] = FALSE;
10061         complete_replace = FALSE;
10062
10063         continue;
10064       }
10065
10066       e = Feld[ex][ey];
10067
10068       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10069         e = MovingOrBlocked2Element(ex, ey);
10070
10071       is_empty = (IS_FREE(ex, ey) ||
10072                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10073
10074       is_walkable     = (is_empty || IS_WALKABLE(e));
10075       is_diggable     = (is_empty || IS_DIGGABLE(e));
10076       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10077       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10078       is_removable    = (is_diggable || is_collectible);
10079
10080       can_replace[xx][yy] =
10081         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10082           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10083           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10084           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10085           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10086           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10087          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10088
10089       if (!can_replace[xx][yy])
10090         complete_replace = FALSE;
10091     }
10092
10093     if (!change->only_if_complete || complete_replace)
10094     {
10095       boolean something_has_changed = FALSE;
10096
10097       if (change->only_if_complete && change->use_random_replace &&
10098           RND(100) < change->random_percentage)
10099         return FALSE;
10100
10101       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10102       {
10103         int ex = x + xx - 1;
10104         int ey = y + yy - 1;
10105         int content_element;
10106
10107         if (can_replace[xx][yy] && (!change->use_random_replace ||
10108                                     RND(100) < change->random_percentage))
10109         {
10110           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10111             RemoveMovingField(ex, ey);
10112
10113           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10114
10115           content_element = change->target_content.e[xx][yy];
10116           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10117                                               ce_value, ce_score);
10118
10119           CreateElementFromChange(ex, ey, target_element);
10120
10121           something_has_changed = TRUE;
10122
10123           /* for symmetry reasons, freeze newly created border elements */
10124           if (ex != x || ey != y)
10125             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10126         }
10127       }
10128
10129       if (something_has_changed)
10130       {
10131         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10132         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10133       }
10134     }
10135   }
10136   else
10137   {
10138     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10139                                         ce_value, ce_score);
10140
10141     if (element == EL_DIAGONAL_GROWING ||
10142         element == EL_DIAGONAL_SHRINKING)
10143     {
10144       target_element = Store[x][y];
10145
10146       Store[x][y] = EL_EMPTY;
10147     }
10148
10149     CreateElementFromChange(x, y, target_element);
10150
10151     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10152     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10153   }
10154
10155   /* this uses direct change before indirect change */
10156   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10157
10158   return TRUE;
10159 }
10160
10161 static void HandleElementChange(int x, int y, int page)
10162 {
10163   int element = MovingOrBlocked2Element(x, y);
10164   struct ElementInfo *ei = &element_info[element];
10165   struct ElementChangeInfo *change = &ei->change_page[page];
10166   boolean handle_action_before_change = FALSE;
10167
10168 #ifdef DEBUG
10169   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10170       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10171   {
10172     printf("\n\n");
10173     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10174            x, y, element, element_info[element].token_name);
10175     printf("HandleElementChange(): This should never happen!\n");
10176     printf("\n\n");
10177   }
10178 #endif
10179
10180   /* this can happen with classic bombs on walkable, changing elements */
10181   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10182   {
10183     return;
10184   }
10185
10186   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10187   {
10188     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10189
10190     if (change->can_change)
10191     {
10192       /* !!! not clear why graphic animation should be reset at all here !!! */
10193       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10194       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10195
10196       /*
10197         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10198
10199         When using an animation frame delay of 1 (this only happens with
10200         "sp_zonk.moving.left/right" in the classic graphics), the default
10201         (non-moving) animation shows wrong animation frames (while the
10202         moving animation, like "sp_zonk.moving.left/right", is correct,
10203         so this graphical bug never shows up with the classic graphics).
10204         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10205         be drawn instead of the correct frames 0,1,2,3. This is caused by
10206         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10207         an element change: First when the change delay ("ChangeDelay[][]")
10208         counter has reached zero after decrementing, then a second time in
10209         the next frame (after "GfxFrame[][]" was already incremented) when
10210         "ChangeDelay[][]" is reset to the initial delay value again.
10211
10212         This causes frame 0 to be drawn twice, while the last frame won't
10213         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10214
10215         As some animations may already be cleverly designed around this bug
10216         (at least the "Snake Bite" snake tail animation does this), it cannot
10217         simply be fixed here without breaking such existing animations.
10218         Unfortunately, it cannot easily be detected if a graphics set was
10219         designed "before" or "after" the bug was fixed. As a workaround,
10220         a new graphics set option "game.graphics_engine_version" was added
10221         to be able to specify the game's major release version for which the
10222         graphics set was designed, which can then be used to decide if the
10223         bugfix should be used (version 4 and above) or not (version 3 or
10224         below, or if no version was specified at all, as with old sets).
10225
10226         (The wrong/fixed animation frames can be tested with the test level set
10227         "test_gfxframe" and level "000", which contains a specially prepared
10228         custom element at level position (x/y) == (11/9) which uses the zonk
10229         animation mentioned above. Using "game.graphics_engine_version: 4"
10230         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10231         This can also be seen from the debug output for this test element.)
10232       */
10233
10234       /* when a custom element is about to change (for example by change delay),
10235          do not reset graphic animation when the custom element is moving */
10236       if (game.graphics_engine_version < 4 &&
10237           !IS_MOVING(x, y))
10238       {
10239         ResetGfxAnimation(x, y);
10240         ResetRandomAnimationValue(x, y);
10241       }
10242
10243       if (change->pre_change_function)
10244         change->pre_change_function(x, y);
10245     }
10246   }
10247
10248   ChangeDelay[x][y]--;
10249
10250   if (ChangeDelay[x][y] != 0)           /* continue element change */
10251   {
10252     if (change->can_change)
10253     {
10254       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10255
10256       if (IS_ANIMATED(graphic))
10257         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10258
10259       if (change->change_function)
10260         change->change_function(x, y);
10261     }
10262   }
10263   else                                  /* finish element change */
10264   {
10265     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10266     {
10267       page = ChangePage[x][y];
10268       ChangePage[x][y] = -1;
10269
10270       change = &ei->change_page[page];
10271     }
10272
10273     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10274     {
10275       ChangeDelay[x][y] = 1;            /* try change after next move step */
10276       ChangePage[x][y] = page;          /* remember page to use for change */
10277
10278       return;
10279     }
10280
10281     /* special case: set new level random seed before changing element */
10282     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10283       handle_action_before_change = TRUE;
10284
10285     if (change->has_action && handle_action_before_change)
10286       ExecuteCustomElementAction(x, y, element, page);
10287
10288     if (change->can_change)
10289     {
10290       if (ChangeElement(x, y, element, page))
10291       {
10292         if (change->post_change_function)
10293           change->post_change_function(x, y);
10294       }
10295     }
10296
10297     if (change->has_action && !handle_action_before_change)
10298       ExecuteCustomElementAction(x, y, element, page);
10299   }
10300 }
10301
10302 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10303                                               int trigger_element,
10304                                               int trigger_event,
10305                                               int trigger_player,
10306                                               int trigger_side,
10307                                               int trigger_page)
10308 {
10309   boolean change_done_any = FALSE;
10310   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10311   int i;
10312
10313   if (!(trigger_events[trigger_element][trigger_event]))
10314     return FALSE;
10315
10316   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10317
10318   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10319   {
10320     int element = EL_CUSTOM_START + i;
10321     boolean change_done = FALSE;
10322     int p;
10323
10324     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10325         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10326       continue;
10327
10328     for (p = 0; p < element_info[element].num_change_pages; p++)
10329     {
10330       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10331
10332       if (change->can_change_or_has_action &&
10333           change->has_event[trigger_event] &&
10334           change->trigger_side & trigger_side &&
10335           change->trigger_player & trigger_player &&
10336           change->trigger_page & trigger_page_bits &&
10337           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10338       {
10339         change->actual_trigger_element = trigger_element;
10340         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10341         change->actual_trigger_player_bits = trigger_player;
10342         change->actual_trigger_side = trigger_side;
10343         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10344         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10345
10346         if ((change->can_change && !change_done) || change->has_action)
10347         {
10348           int x, y;
10349
10350           SCAN_PLAYFIELD(x, y)
10351           {
10352             if (Feld[x][y] == element)
10353             {
10354               if (change->can_change && !change_done)
10355               {
10356                 /* if element already changed in this frame, not only prevent
10357                    another element change (checked in ChangeElement()), but
10358                    also prevent additional element actions for this element */
10359
10360                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10361                     !level.use_action_after_change_bug)
10362                   continue;
10363
10364                 ChangeDelay[x][y] = 1;
10365                 ChangeEvent[x][y] = trigger_event;
10366
10367                 HandleElementChange(x, y, p);
10368               }
10369               else if (change->has_action)
10370               {
10371                 /* if element already changed in this frame, not only prevent
10372                    another element change (checked in ChangeElement()), but
10373                    also prevent additional element actions for this element */
10374
10375                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10376                     !level.use_action_after_change_bug)
10377                   continue;
10378
10379                 ExecuteCustomElementAction(x, y, element, p);
10380                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10381               }
10382             }
10383           }
10384
10385           if (change->can_change)
10386           {
10387             change_done = TRUE;
10388             change_done_any = TRUE;
10389           }
10390         }
10391       }
10392     }
10393   }
10394
10395   RECURSION_LOOP_DETECTION_END();
10396
10397   return change_done_any;
10398 }
10399
10400 static boolean CheckElementChangeExt(int x, int y,
10401                                      int element,
10402                                      int trigger_element,
10403                                      int trigger_event,
10404                                      int trigger_player,
10405                                      int trigger_side)
10406 {
10407   boolean change_done = FALSE;
10408   int p;
10409
10410   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10411       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10412     return FALSE;
10413
10414   if (Feld[x][y] == EL_BLOCKED)
10415   {
10416     Blocked2Moving(x, y, &x, &y);
10417     element = Feld[x][y];
10418   }
10419
10420   /* check if element has already changed or is about to change after moving */
10421   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10422        Feld[x][y] != element) ||
10423
10424       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10425        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10426         ChangePage[x][y] != -1)))
10427     return FALSE;
10428
10429   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10430
10431   for (p = 0; p < element_info[element].num_change_pages; p++)
10432   {
10433     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10434
10435     /* check trigger element for all events where the element that is checked
10436        for changing interacts with a directly adjacent element -- this is
10437        different to element changes that affect other elements to change on the
10438        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10439     boolean check_trigger_element =
10440       (trigger_event == CE_TOUCHING_X ||
10441        trigger_event == CE_HITTING_X ||
10442        trigger_event == CE_HIT_BY_X ||
10443        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10444
10445     if (change->can_change_or_has_action &&
10446         change->has_event[trigger_event] &&
10447         change->trigger_side & trigger_side &&
10448         change->trigger_player & trigger_player &&
10449         (!check_trigger_element ||
10450          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10451     {
10452       change->actual_trigger_element = trigger_element;
10453       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10454       change->actual_trigger_player_bits = trigger_player;
10455       change->actual_trigger_side = trigger_side;
10456       change->actual_trigger_ce_value = CustomValue[x][y];
10457       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10458
10459       /* special case: trigger element not at (x,y) position for some events */
10460       if (check_trigger_element)
10461       {
10462         static struct
10463         {
10464           int dx, dy;
10465         } move_xy[] =
10466           {
10467             {  0,  0 },
10468             { -1,  0 },
10469             { +1,  0 },
10470             {  0,  0 },
10471             {  0, -1 },
10472             {  0,  0 }, { 0, 0 }, { 0, 0 },
10473             {  0, +1 }
10474           };
10475
10476         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10477         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10478
10479         change->actual_trigger_ce_value = CustomValue[xx][yy];
10480         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10481       }
10482
10483       if (change->can_change && !change_done)
10484       {
10485         ChangeDelay[x][y] = 1;
10486         ChangeEvent[x][y] = trigger_event;
10487
10488         HandleElementChange(x, y, p);
10489
10490         change_done = TRUE;
10491       }
10492       else if (change->has_action)
10493       {
10494         ExecuteCustomElementAction(x, y, element, p);
10495         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10496       }
10497     }
10498   }
10499
10500   RECURSION_LOOP_DETECTION_END();
10501
10502   return change_done;
10503 }
10504
10505 static void PlayPlayerSound(struct PlayerInfo *player)
10506 {
10507   int jx = player->jx, jy = player->jy;
10508   int sound_element = player->artwork_element;
10509   int last_action = player->last_action_waiting;
10510   int action = player->action_waiting;
10511
10512   if (player->is_waiting)
10513   {
10514     if (action != last_action)
10515       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10516     else
10517       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10518   }
10519   else
10520   {
10521     if (action != last_action)
10522       StopSound(element_info[sound_element].sound[last_action]);
10523
10524     if (last_action == ACTION_SLEEPING)
10525       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10526   }
10527 }
10528
10529 static void PlayAllPlayersSound()
10530 {
10531   int i;
10532
10533   for (i = 0; i < MAX_PLAYERS; i++)
10534     if (stored_player[i].active)
10535       PlayPlayerSound(&stored_player[i]);
10536 }
10537
10538 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10539 {
10540   boolean last_waiting = player->is_waiting;
10541   int move_dir = player->MovDir;
10542
10543   player->dir_waiting = move_dir;
10544   player->last_action_waiting = player->action_waiting;
10545
10546   if (is_waiting)
10547   {
10548     if (!last_waiting)          /* not waiting -> waiting */
10549     {
10550       player->is_waiting = TRUE;
10551
10552       player->frame_counter_bored =
10553         FrameCounter +
10554         game.player_boring_delay_fixed +
10555         GetSimpleRandom(game.player_boring_delay_random);
10556       player->frame_counter_sleeping =
10557         FrameCounter +
10558         game.player_sleeping_delay_fixed +
10559         GetSimpleRandom(game.player_sleeping_delay_random);
10560
10561       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10562     }
10563
10564     if (game.player_sleeping_delay_fixed +
10565         game.player_sleeping_delay_random > 0 &&
10566         player->anim_delay_counter == 0 &&
10567         player->post_delay_counter == 0 &&
10568         FrameCounter >= player->frame_counter_sleeping)
10569       player->is_sleeping = TRUE;
10570     else if (game.player_boring_delay_fixed +
10571              game.player_boring_delay_random > 0 &&
10572              FrameCounter >= player->frame_counter_bored)
10573       player->is_bored = TRUE;
10574
10575     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10576                               player->is_bored ? ACTION_BORING :
10577                               ACTION_WAITING);
10578
10579     if (player->is_sleeping && player->use_murphy)
10580     {
10581       /* special case for sleeping Murphy when leaning against non-free tile */
10582
10583       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10584           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10585            !IS_MOVING(player->jx - 1, player->jy)))
10586         move_dir = MV_LEFT;
10587       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10588                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10589                 !IS_MOVING(player->jx + 1, player->jy)))
10590         move_dir = MV_RIGHT;
10591       else
10592         player->is_sleeping = FALSE;
10593
10594       player->dir_waiting = move_dir;
10595     }
10596
10597     if (player->is_sleeping)
10598     {
10599       if (player->num_special_action_sleeping > 0)
10600       {
10601         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10602         {
10603           int last_special_action = player->special_action_sleeping;
10604           int num_special_action = player->num_special_action_sleeping;
10605           int special_action =
10606             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10607              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10608              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10609              last_special_action + 1 : ACTION_SLEEPING);
10610           int special_graphic =
10611             el_act_dir2img(player->artwork_element, special_action, move_dir);
10612
10613           player->anim_delay_counter =
10614             graphic_info[special_graphic].anim_delay_fixed +
10615             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10616           player->post_delay_counter =
10617             graphic_info[special_graphic].post_delay_fixed +
10618             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10619
10620           player->special_action_sleeping = special_action;
10621         }
10622
10623         if (player->anim_delay_counter > 0)
10624         {
10625           player->action_waiting = player->special_action_sleeping;
10626           player->anim_delay_counter--;
10627         }
10628         else if (player->post_delay_counter > 0)
10629         {
10630           player->post_delay_counter--;
10631         }
10632       }
10633     }
10634     else if (player->is_bored)
10635     {
10636       if (player->num_special_action_bored > 0)
10637       {
10638         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10639         {
10640           int special_action =
10641             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10642           int special_graphic =
10643             el_act_dir2img(player->artwork_element, special_action, move_dir);
10644
10645           player->anim_delay_counter =
10646             graphic_info[special_graphic].anim_delay_fixed +
10647             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10648           player->post_delay_counter =
10649             graphic_info[special_graphic].post_delay_fixed +
10650             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10651
10652           player->special_action_bored = special_action;
10653         }
10654
10655         if (player->anim_delay_counter > 0)
10656         {
10657           player->action_waiting = player->special_action_bored;
10658           player->anim_delay_counter--;
10659         }
10660         else if (player->post_delay_counter > 0)
10661         {
10662           player->post_delay_counter--;
10663         }
10664       }
10665     }
10666   }
10667   else if (last_waiting)        /* waiting -> not waiting */
10668   {
10669     player->is_waiting = FALSE;
10670     player->is_bored = FALSE;
10671     player->is_sleeping = FALSE;
10672
10673     player->frame_counter_bored = -1;
10674     player->frame_counter_sleeping = -1;
10675
10676     player->anim_delay_counter = 0;
10677     player->post_delay_counter = 0;
10678
10679     player->dir_waiting = player->MovDir;
10680     player->action_waiting = ACTION_DEFAULT;
10681
10682     player->special_action_bored = ACTION_DEFAULT;
10683     player->special_action_sleeping = ACTION_DEFAULT;
10684   }
10685 }
10686
10687 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10688 {
10689   static boolean player_was_moving = FALSE;
10690   static boolean player_was_snapping = FALSE;
10691   static boolean player_was_dropping = FALSE;
10692
10693   if ((!player->is_moving  && player_was_moving) ||
10694       (player->MovPos == 0 && player_was_moving) ||
10695       (player->is_snapping && !player_was_snapping) ||
10696       (player->is_dropping && !player_was_dropping))
10697   {
10698     if (!SaveEngineSnapshotToList())
10699       return;
10700
10701     player_was_moving = FALSE;
10702     player_was_snapping = TRUE;
10703     player_was_dropping = TRUE;
10704   }
10705   else
10706   {
10707     if (player->is_moving)
10708       player_was_moving = TRUE;
10709
10710     if (!player->is_snapping)
10711       player_was_snapping = FALSE;
10712
10713     if (!player->is_dropping)
10714       player_was_dropping = FALSE;
10715   }
10716 }
10717
10718 static void CheckSingleStepMode(struct PlayerInfo *player)
10719 {
10720   if (tape.single_step && tape.recording && !tape.pausing)
10721   {
10722     /* as it is called "single step mode", just return to pause mode when the
10723        player stopped moving after one tile (or never starts moving at all) */
10724     if (!player->is_moving && !player->is_pushing)
10725     {
10726       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10727       SnapField(player, 0, 0);                  /* stop snapping */
10728     }
10729   }
10730
10731   CheckSaveEngineSnapshot(player);
10732 }
10733
10734 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10735 {
10736   int left      = player_action & JOY_LEFT;
10737   int right     = player_action & JOY_RIGHT;
10738   int up        = player_action & JOY_UP;
10739   int down      = player_action & JOY_DOWN;
10740   int button1   = player_action & JOY_BUTTON_1;
10741   int button2   = player_action & JOY_BUTTON_2;
10742   int dx        = (left ? -1 : right ? 1 : 0);
10743   int dy        = (up   ? -1 : down  ? 1 : 0);
10744
10745   if (!player->active || tape.pausing)
10746     return 0;
10747
10748   if (player_action)
10749   {
10750     if (button1)
10751       SnapField(player, dx, dy);
10752     else
10753     {
10754       if (button2)
10755         DropElement(player);
10756
10757       MovePlayer(player, dx, dy);
10758     }
10759
10760     CheckSingleStepMode(player);
10761
10762     SetPlayerWaiting(player, FALSE);
10763
10764     return player_action;
10765   }
10766   else
10767   {
10768     /* no actions for this player (no input at player's configured device) */
10769
10770     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10771     SnapField(player, 0, 0);
10772     CheckGravityMovementWhenNotMoving(player);
10773
10774     if (player->MovPos == 0)
10775       SetPlayerWaiting(player, TRUE);
10776
10777     if (player->MovPos == 0)    /* needed for tape.playing */
10778       player->is_moving = FALSE;
10779
10780     player->is_dropping = FALSE;
10781     player->is_dropping_pressed = FALSE;
10782     player->drop_pressed_delay = 0;
10783
10784     CheckSingleStepMode(player);
10785
10786     return 0;
10787   }
10788 }
10789
10790 static void CheckLevelTime()
10791 {
10792   int i;
10793
10794   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10795   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10796   {
10797     if (level.native_em_level->lev->home == 0)  /* all players at home */
10798     {
10799       PlayerWins(local_player);
10800
10801       AllPlayersGone = TRUE;
10802
10803       level.native_em_level->lev->home = -1;
10804     }
10805
10806     if (level.native_em_level->ply[0]->alive == 0 &&
10807         level.native_em_level->ply[1]->alive == 0 &&
10808         level.native_em_level->ply[2]->alive == 0 &&
10809         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10810       AllPlayersGone = TRUE;
10811   }
10812   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10813   {
10814     if (game_sp.LevelSolved &&
10815         !game_sp.GameOver)                              /* game won */
10816     {
10817       PlayerWins(local_player);
10818
10819       game_sp.GameOver = TRUE;
10820
10821       AllPlayersGone = TRUE;
10822     }
10823
10824     if (game_sp.GameOver)                               /* game lost */
10825       AllPlayersGone = TRUE;
10826   }
10827
10828   if (TimeFrames >= FRAMES_PER_SECOND)
10829   {
10830     TimeFrames = 0;
10831     TapeTime++;
10832
10833     for (i = 0; i < MAX_PLAYERS; i++)
10834     {
10835       struct PlayerInfo *player = &stored_player[i];
10836
10837       if (SHIELD_ON(player))
10838       {
10839         player->shield_normal_time_left--;
10840
10841         if (player->shield_deadly_time_left > 0)
10842           player->shield_deadly_time_left--;
10843       }
10844     }
10845
10846     if (!local_player->LevelSolved && !level.use_step_counter)
10847     {
10848       TimePlayed++;
10849
10850       if (TimeLeft > 0)
10851       {
10852         TimeLeft--;
10853
10854         if (TimeLeft <= 10 && setup.time_limit)
10855           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10856
10857         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10858            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10859
10860         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10861
10862         if (!TimeLeft && setup.time_limit)
10863         {
10864           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10865             level.native_em_level->lev->killed_out_of_time = TRUE;
10866           else
10867             for (i = 0; i < MAX_PLAYERS; i++)
10868               KillPlayer(&stored_player[i]);
10869         }
10870       }
10871       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10872       {
10873         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10874       }
10875
10876       level.native_em_level->lev->time =
10877         (game.no_time_limit ? TimePlayed : TimeLeft);
10878     }
10879
10880     if (tape.recording || tape.playing)
10881       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10882   }
10883
10884   if (tape.recording || tape.playing)
10885     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10886
10887   UpdateAndDisplayGameControlValues();
10888 }
10889
10890 void AdvanceFrameAndPlayerCounters(int player_nr)
10891 {
10892   int i;
10893
10894   /* advance frame counters (global frame counter and time frame counter) */
10895   FrameCounter++;
10896   TimeFrames++;
10897
10898   /* advance player counters (counters for move delay, move animation etc.) */
10899   for (i = 0; i < MAX_PLAYERS; i++)
10900   {
10901     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10902     int move_delay_value = stored_player[i].move_delay_value;
10903     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10904
10905     if (!advance_player_counters)       /* not all players may be affected */
10906       continue;
10907
10908     if (move_frames == 0)       /* less than one move per game frame */
10909     {
10910       int stepsize = TILEX / move_delay_value;
10911       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10912       int count = (stored_player[i].is_moving ?
10913                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10914
10915       if (count % delay == 0)
10916         move_frames = 1;
10917     }
10918
10919     stored_player[i].Frame += move_frames;
10920
10921     if (stored_player[i].MovPos != 0)
10922       stored_player[i].StepFrame += move_frames;
10923
10924     if (stored_player[i].move_delay > 0)
10925       stored_player[i].move_delay--;
10926
10927     /* due to bugs in previous versions, counter must count up, not down */
10928     if (stored_player[i].push_delay != -1)
10929       stored_player[i].push_delay++;
10930
10931     if (stored_player[i].drop_delay > 0)
10932       stored_player[i].drop_delay--;
10933
10934     if (stored_player[i].is_dropping_pressed)
10935       stored_player[i].drop_pressed_delay++;
10936   }
10937 }
10938
10939 void StartGameActions(boolean init_network_game, boolean record_tape,
10940                       int random_seed)
10941 {
10942   unsigned int new_random_seed = InitRND(random_seed);
10943
10944   if (record_tape)
10945     TapeStartRecording(new_random_seed);
10946
10947 #if defined(NETWORK_AVALIABLE)
10948   if (init_network_game)
10949   {
10950     SendToServer_StartPlaying();
10951
10952     return;
10953   }
10954 #endif
10955
10956   InitGame();
10957 }
10958
10959 void GameActions()
10960 {
10961   static unsigned int game_frame_delay = 0;
10962   unsigned int game_frame_delay_value;
10963   byte *recorded_player_action;
10964   byte summarized_player_action = 0;
10965   byte tape_action[MAX_PLAYERS];
10966   int i;
10967
10968   /* detect endless loops, caused by custom element programming */
10969   if (recursion_loop_detected && recursion_loop_depth == 0)
10970   {
10971     char *message = getStringCat3("Internal Error! Element ",
10972                                   EL_NAME(recursion_loop_element),
10973                                   " caused endless loop! Quit the game?");
10974
10975     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10976           EL_NAME(recursion_loop_element));
10977
10978     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10979
10980     recursion_loop_detected = FALSE;    /* if game should be continued */
10981
10982     free(message);
10983
10984     return;
10985   }
10986
10987   if (game.restart_level)
10988     StartGameActions(options.network, setup.autorecord, level.random_seed);
10989
10990   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10991   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10992   {
10993     if (level.native_em_level->lev->home == 0)  /* all players at home */
10994     {
10995       PlayerWins(local_player);
10996
10997       AllPlayersGone = TRUE;
10998
10999       level.native_em_level->lev->home = -1;
11000     }
11001
11002     if (level.native_em_level->ply[0]->alive == 0 &&
11003         level.native_em_level->ply[1]->alive == 0 &&
11004         level.native_em_level->ply[2]->alive == 0 &&
11005         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11006       AllPlayersGone = TRUE;
11007   }
11008   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11009   {
11010     if (game_sp.LevelSolved &&
11011         !game_sp.GameOver)                              /* game won */
11012     {
11013       PlayerWins(local_player);
11014
11015       game_sp.GameOver = TRUE;
11016
11017       AllPlayersGone = TRUE;
11018     }
11019
11020     if (game_sp.GameOver)                               /* game lost */
11021       AllPlayersGone = TRUE;
11022   }
11023
11024   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11025     GameWon();
11026
11027   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11028     TapeStop();
11029
11030   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11031     return;
11032
11033   game_frame_delay_value =
11034     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11035
11036   if (tape.playing && tape.warp_forward && !tape.pausing)
11037     game_frame_delay_value = 0;
11038
11039 #if 0
11040   /* ---------- main game synchronization point ---------- */
11041
11042   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11043
11044   printf("::: skip == %d\n", skip);
11045
11046 #else
11047   /* ---------- main game synchronization point ---------- */
11048
11049   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11050 #endif
11051
11052   if (network_playing && !network_player_action_received)
11053   {
11054     /* try to get network player actions in time */
11055
11056 #if defined(NETWORK_AVALIABLE)
11057     /* last chance to get network player actions without main loop delay */
11058     HandleNetworking();
11059 #endif
11060
11061     /* game was quit by network peer */
11062     if (game_status != GAME_MODE_PLAYING)
11063       return;
11064
11065     if (!network_player_action_received)
11066       return;           /* failed to get network player actions in time */
11067
11068     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11069   }
11070
11071   if (tape.pausing)
11072     return;
11073
11074   /* at this point we know that we really continue executing the game */
11075
11076   network_player_action_received = FALSE;
11077
11078   /* when playing tape, read previously recorded player input from tape data */
11079   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11080
11081   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11082   if (tape.pausing)
11083     return;
11084
11085   if (tape.set_centered_player)
11086   {
11087     game.centered_player_nr_next = tape.centered_player_nr_next;
11088     game.set_centered_player = TRUE;
11089   }
11090
11091   for (i = 0; i < MAX_PLAYERS; i++)
11092   {
11093     summarized_player_action |= stored_player[i].action;
11094
11095     if (!network_playing && (game.team_mode || tape.playing))
11096       stored_player[i].effective_action = stored_player[i].action;
11097   }
11098
11099 #if defined(NETWORK_AVALIABLE)
11100   if (network_playing)
11101     SendToServer_MovePlayer(summarized_player_action);
11102 #endif
11103
11104   // summarize all actions at local players mapped input device position
11105   // (this allows using different input devices in single player mode)
11106   if (!options.network && !game.team_mode)
11107     stored_player[map_player_action[local_player->index_nr]].effective_action =
11108       summarized_player_action;
11109
11110   if (tape.recording &&
11111       setup.team_mode &&
11112       setup.input_on_focus &&
11113       game.centered_player_nr != -1)
11114   {
11115     for (i = 0; i < MAX_PLAYERS; i++)
11116       stored_player[i].effective_action =
11117         (i == game.centered_player_nr ? summarized_player_action : 0);
11118   }
11119
11120   if (recorded_player_action != NULL)
11121     for (i = 0; i < MAX_PLAYERS; i++)
11122       stored_player[i].effective_action = recorded_player_action[i];
11123
11124   for (i = 0; i < MAX_PLAYERS; i++)
11125   {
11126     tape_action[i] = stored_player[i].effective_action;
11127
11128     /* (this may happen in the RND game engine if a player was not present on
11129        the playfield on level start, but appeared later from a custom element */
11130     if (setup.team_mode &&
11131         tape.recording &&
11132         tape_action[i] &&
11133         !tape.player_participates[i])
11134       tape.player_participates[i] = TRUE;
11135   }
11136
11137   /* only record actions from input devices, but not programmed actions */
11138   if (tape.recording)
11139     TapeRecordAction(tape_action);
11140
11141 #if USE_NEW_PLAYER_ASSIGNMENTS
11142   // !!! also map player actions in single player mode !!!
11143   // if (game.team_mode)
11144   if (1)
11145   {
11146     byte mapped_action[MAX_PLAYERS];
11147
11148 #if DEBUG_PLAYER_ACTIONS
11149     printf(":::");
11150     for (i = 0; i < MAX_PLAYERS; i++)
11151       printf(" %d, ", stored_player[i].effective_action);
11152 #endif
11153
11154     for (i = 0; i < MAX_PLAYERS; i++)
11155       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11156
11157     for (i = 0; i < MAX_PLAYERS; i++)
11158       stored_player[i].effective_action = mapped_action[i];
11159
11160 #if DEBUG_PLAYER_ACTIONS
11161     printf(" =>");
11162     for (i = 0; i < MAX_PLAYERS; i++)
11163       printf(" %d, ", stored_player[i].effective_action);
11164     printf("\n");
11165 #endif
11166   }
11167 #if DEBUG_PLAYER_ACTIONS
11168   else
11169   {
11170     printf(":::");
11171     for (i = 0; i < MAX_PLAYERS; i++)
11172       printf(" %d, ", stored_player[i].effective_action);
11173     printf("\n");
11174   }
11175 #endif
11176 #endif
11177
11178   for (i = 0; i < MAX_PLAYERS; i++)
11179   {
11180     // allow engine snapshot in case of changed movement attempt
11181     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11182         (stored_player[i].effective_action & KEY_MOTION))
11183       game.snapshot.changed_action = TRUE;
11184
11185     // allow engine snapshot in case of snapping/dropping attempt
11186     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11187         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11188       game.snapshot.changed_action = TRUE;
11189
11190     game.snapshot.last_action[i] = stored_player[i].effective_action;
11191   }
11192
11193   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11194   {
11195     GameActions_EM_Main();
11196   }
11197   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11198   {
11199     GameActions_SP_Main();
11200   }
11201   else
11202   {
11203     GameActions_RND_Main();
11204   }
11205
11206   BlitScreenToBitmap(backbuffer);
11207
11208   CheckLevelTime();
11209
11210   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11211
11212   if (options.debug)                    /* calculate frames per second */
11213   {
11214     static unsigned int fps_counter = 0;
11215     static int fps_frames = 0;
11216     unsigned int fps_delay_ms = Counter() - fps_counter;
11217
11218     fps_frames++;
11219
11220     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11221     {
11222       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11223
11224       fps_frames = 0;
11225       fps_counter = Counter();
11226     }
11227
11228     redraw_mask |= REDRAW_FPS;
11229   }
11230 }
11231
11232 void GameActions_EM_Main()
11233 {
11234   byte effective_action[MAX_PLAYERS];
11235   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11236   int i;
11237
11238   for (i = 0; i < MAX_PLAYERS; i++)
11239     effective_action[i] = stored_player[i].effective_action;
11240
11241   GameActions_EM(effective_action, warp_mode);
11242 }
11243
11244 void GameActions_SP_Main()
11245 {
11246   byte effective_action[MAX_PLAYERS];
11247   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11248   int i;
11249
11250   for (i = 0; i < MAX_PLAYERS; i++)
11251     effective_action[i] = stored_player[i].effective_action;
11252
11253   GameActions_SP(effective_action, warp_mode);
11254 }
11255
11256 void GameActions_RND_Main()
11257 {
11258   GameActions_RND();
11259 }
11260
11261 void GameActions_RND()
11262 {
11263   int magic_wall_x = 0, magic_wall_y = 0;
11264   int i, x, y, element, graphic;
11265
11266   InitPlayfieldScanModeVars();
11267
11268   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11269   {
11270     SCAN_PLAYFIELD(x, y)
11271     {
11272       ChangeCount[x][y] = 0;
11273       ChangeEvent[x][y] = -1;
11274     }
11275   }
11276
11277   if (game.set_centered_player)
11278   {
11279     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11280
11281     /* switching to "all players" only possible if all players fit to screen */
11282     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11283     {
11284       game.centered_player_nr_next = game.centered_player_nr;
11285       game.set_centered_player = FALSE;
11286     }
11287
11288     /* do not switch focus to non-existing (or non-active) player */
11289     if (game.centered_player_nr_next >= 0 &&
11290         !stored_player[game.centered_player_nr_next].active)
11291     {
11292       game.centered_player_nr_next = game.centered_player_nr;
11293       game.set_centered_player = FALSE;
11294     }
11295   }
11296
11297   if (game.set_centered_player &&
11298       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11299   {
11300     int sx, sy;
11301
11302     if (game.centered_player_nr_next == -1)
11303     {
11304       setScreenCenteredToAllPlayers(&sx, &sy);
11305     }
11306     else
11307     {
11308       sx = stored_player[game.centered_player_nr_next].jx;
11309       sy = stored_player[game.centered_player_nr_next].jy;
11310     }
11311
11312     game.centered_player_nr = game.centered_player_nr_next;
11313     game.set_centered_player = FALSE;
11314
11315     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11316     DrawGameDoorValues();
11317   }
11318
11319   for (i = 0; i < MAX_PLAYERS; i++)
11320   {
11321     int actual_player_action = stored_player[i].effective_action;
11322
11323 #if 1
11324     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11325        - rnd_equinox_tetrachloride 048
11326        - rnd_equinox_tetrachloride_ii 096
11327        - rnd_emanuel_schmieg 002
11328        - doctor_sloan_ww 001, 020
11329     */
11330     if (stored_player[i].MovPos == 0)
11331       CheckGravityMovement(&stored_player[i]);
11332 #endif
11333
11334     /* overwrite programmed action with tape action */
11335     if (stored_player[i].programmed_action)
11336       actual_player_action = stored_player[i].programmed_action;
11337
11338     PlayerActions(&stored_player[i], actual_player_action);
11339
11340     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11341   }
11342
11343   ScrollScreen(NULL, SCROLL_GO_ON);
11344
11345   /* for backwards compatibility, the following code emulates a fixed bug that
11346      occured when pushing elements (causing elements that just made their last
11347      pushing step to already (if possible) make their first falling step in the
11348      same game frame, which is bad); this code is also needed to use the famous
11349      "spring push bug" which is used in older levels and might be wanted to be
11350      used also in newer levels, but in this case the buggy pushing code is only
11351      affecting the "spring" element and no other elements */
11352
11353   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11354   {
11355     for (i = 0; i < MAX_PLAYERS; i++)
11356     {
11357       struct PlayerInfo *player = &stored_player[i];
11358       int x = player->jx;
11359       int y = player->jy;
11360
11361       if (player->active && player->is_pushing && player->is_moving &&
11362           IS_MOVING(x, y) &&
11363           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11364            Feld[x][y] == EL_SPRING))
11365       {
11366         ContinueMoving(x, y);
11367
11368         /* continue moving after pushing (this is actually a bug) */
11369         if (!IS_MOVING(x, y))
11370           Stop[x][y] = FALSE;
11371       }
11372     }
11373   }
11374
11375   SCAN_PLAYFIELD(x, y)
11376   {
11377     ChangeCount[x][y] = 0;
11378     ChangeEvent[x][y] = -1;
11379
11380     /* this must be handled before main playfield loop */
11381     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11382     {
11383       MovDelay[x][y]--;
11384       if (MovDelay[x][y] <= 0)
11385         RemoveField(x, y);
11386     }
11387
11388     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11389     {
11390       MovDelay[x][y]--;
11391       if (MovDelay[x][y] <= 0)
11392       {
11393         RemoveField(x, y);
11394         TEST_DrawLevelField(x, y);
11395
11396         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11397       }
11398     }
11399
11400 #if DEBUG
11401     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11402     {
11403       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11404       printf("GameActions(): This should never happen!\n");
11405
11406       ChangePage[x][y] = -1;
11407     }
11408 #endif
11409
11410     Stop[x][y] = FALSE;
11411     if (WasJustMoving[x][y] > 0)
11412       WasJustMoving[x][y]--;
11413     if (WasJustFalling[x][y] > 0)
11414       WasJustFalling[x][y]--;
11415     if (CheckCollision[x][y] > 0)
11416       CheckCollision[x][y]--;
11417     if (CheckImpact[x][y] > 0)
11418       CheckImpact[x][y]--;
11419
11420     GfxFrame[x][y]++;
11421
11422     /* reset finished pushing action (not done in ContinueMoving() to allow
11423        continuous pushing animation for elements with zero push delay) */
11424     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11425     {
11426       ResetGfxAnimation(x, y);
11427       TEST_DrawLevelField(x, y);
11428     }
11429
11430 #if DEBUG
11431     if (IS_BLOCKED(x, y))
11432     {
11433       int oldx, oldy;
11434
11435       Blocked2Moving(x, y, &oldx, &oldy);
11436       if (!IS_MOVING(oldx, oldy))
11437       {
11438         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11439         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11440         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11441         printf("GameActions(): This should never happen!\n");
11442       }
11443     }
11444 #endif
11445   }
11446
11447   SCAN_PLAYFIELD(x, y)
11448   {
11449     element = Feld[x][y];
11450     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11451
11452     ResetGfxFrame(x, y, TRUE);
11453
11454     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11455         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11456       ResetRandomAnimationValue(x, y);
11457
11458     SetRandomAnimationValue(x, y);
11459
11460     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11461
11462     if (IS_INACTIVE(element))
11463     {
11464       if (IS_ANIMATED(graphic))
11465         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11466
11467       continue;
11468     }
11469
11470     /* this may take place after moving, so 'element' may have changed */
11471     if (IS_CHANGING(x, y) &&
11472         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11473     {
11474       int page = element_info[element].event_page_nr[CE_DELAY];
11475
11476       HandleElementChange(x, y, page);
11477
11478       element = Feld[x][y];
11479       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11480     }
11481
11482     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11483     {
11484       StartMoving(x, y);
11485
11486       element = Feld[x][y];
11487       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11488
11489       if (IS_ANIMATED(graphic) &&
11490           !IS_MOVING(x, y) &&
11491           !Stop[x][y])
11492         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11493
11494       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11495         TEST_DrawTwinkleOnField(x, y);
11496     }
11497     else if ((element == EL_ACID ||
11498               element == EL_EXIT_OPEN ||
11499               element == EL_EM_EXIT_OPEN ||
11500               element == EL_SP_EXIT_OPEN ||
11501               element == EL_STEEL_EXIT_OPEN ||
11502               element == EL_EM_STEEL_EXIT_OPEN ||
11503               element == EL_SP_TERMINAL ||
11504               element == EL_SP_TERMINAL_ACTIVE ||
11505               element == EL_EXTRA_TIME ||
11506               element == EL_SHIELD_NORMAL ||
11507               element == EL_SHIELD_DEADLY) &&
11508              IS_ANIMATED(graphic))
11509       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11510     else if (IS_MOVING(x, y))
11511       ContinueMoving(x, y);
11512     else if (IS_ACTIVE_BOMB(element))
11513       CheckDynamite(x, y);
11514     else if (element == EL_AMOEBA_GROWING)
11515       AmoebeWaechst(x, y);
11516     else if (element == EL_AMOEBA_SHRINKING)
11517       AmoebaDisappearing(x, y);
11518
11519 #if !USE_NEW_AMOEBA_CODE
11520     else if (IS_AMOEBALIVE(element))
11521       AmoebeAbleger(x, y);
11522 #endif
11523
11524     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11525       Life(x, y);
11526     else if (element == EL_EXIT_CLOSED)
11527       CheckExit(x, y);
11528     else if (element == EL_EM_EXIT_CLOSED)
11529       CheckExitEM(x, y);
11530     else if (element == EL_STEEL_EXIT_CLOSED)
11531       CheckExitSteel(x, y);
11532     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11533       CheckExitSteelEM(x, y);
11534     else if (element == EL_SP_EXIT_CLOSED)
11535       CheckExitSP(x, y);
11536     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11537              element == EL_EXPANDABLE_STEELWALL_GROWING)
11538       MauerWaechst(x, y);
11539     else if (element == EL_EXPANDABLE_WALL ||
11540              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11541              element == EL_EXPANDABLE_WALL_VERTICAL ||
11542              element == EL_EXPANDABLE_WALL_ANY ||
11543              element == EL_BD_EXPANDABLE_WALL)
11544       MauerAbleger(x, y);
11545     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11546              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11547              element == EL_EXPANDABLE_STEELWALL_ANY)
11548       MauerAblegerStahl(x, y);
11549     else if (element == EL_FLAMES)
11550       CheckForDragon(x, y);
11551     else if (element == EL_EXPLOSION)
11552       ; /* drawing of correct explosion animation is handled separately */
11553     else if (element == EL_ELEMENT_SNAPPING ||
11554              element == EL_DIAGONAL_SHRINKING ||
11555              element == EL_DIAGONAL_GROWING)
11556     {
11557       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11558
11559       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11560     }
11561     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11562       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11563
11564     if (IS_BELT_ACTIVE(element))
11565       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11566
11567     if (game.magic_wall_active)
11568     {
11569       int jx = local_player->jx, jy = local_player->jy;
11570
11571       /* play the element sound at the position nearest to the player */
11572       if ((element == EL_MAGIC_WALL_FULL ||
11573            element == EL_MAGIC_WALL_ACTIVE ||
11574            element == EL_MAGIC_WALL_EMPTYING ||
11575            element == EL_BD_MAGIC_WALL_FULL ||
11576            element == EL_BD_MAGIC_WALL_ACTIVE ||
11577            element == EL_BD_MAGIC_WALL_EMPTYING ||
11578            element == EL_DC_MAGIC_WALL_FULL ||
11579            element == EL_DC_MAGIC_WALL_ACTIVE ||
11580            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11581           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11582       {
11583         magic_wall_x = x;
11584         magic_wall_y = y;
11585       }
11586     }
11587   }
11588
11589 #if USE_NEW_AMOEBA_CODE
11590   /* new experimental amoeba growth stuff */
11591   if (!(FrameCounter % 8))
11592   {
11593     static unsigned int random = 1684108901;
11594
11595     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11596     {
11597       x = RND(lev_fieldx);
11598       y = RND(lev_fieldy);
11599       element = Feld[x][y];
11600
11601       if (!IS_PLAYER(x,y) &&
11602           (element == EL_EMPTY ||
11603            CAN_GROW_INTO(element) ||
11604            element == EL_QUICKSAND_EMPTY ||
11605            element == EL_QUICKSAND_FAST_EMPTY ||
11606            element == EL_ACID_SPLASH_LEFT ||
11607            element == EL_ACID_SPLASH_RIGHT))
11608       {
11609         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11610             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11611             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11612             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11613           Feld[x][y] = EL_AMOEBA_DROP;
11614       }
11615
11616       random = random * 129 + 1;
11617     }
11618   }
11619 #endif
11620
11621   game.explosions_delayed = FALSE;
11622
11623   SCAN_PLAYFIELD(x, y)
11624   {
11625     element = Feld[x][y];
11626
11627     if (ExplodeField[x][y])
11628       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11629     else if (element == EL_EXPLOSION)
11630       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11631
11632     ExplodeField[x][y] = EX_TYPE_NONE;
11633   }
11634
11635   game.explosions_delayed = TRUE;
11636
11637   if (game.magic_wall_active)
11638   {
11639     if (!(game.magic_wall_time_left % 4))
11640     {
11641       int element = Feld[magic_wall_x][magic_wall_y];
11642
11643       if (element == EL_BD_MAGIC_WALL_FULL ||
11644           element == EL_BD_MAGIC_WALL_ACTIVE ||
11645           element == EL_BD_MAGIC_WALL_EMPTYING)
11646         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11647       else if (element == EL_DC_MAGIC_WALL_FULL ||
11648                element == EL_DC_MAGIC_WALL_ACTIVE ||
11649                element == EL_DC_MAGIC_WALL_EMPTYING)
11650         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11651       else
11652         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11653     }
11654
11655     if (game.magic_wall_time_left > 0)
11656     {
11657       game.magic_wall_time_left--;
11658
11659       if (!game.magic_wall_time_left)
11660       {
11661         SCAN_PLAYFIELD(x, y)
11662         {
11663           element = Feld[x][y];
11664
11665           if (element == EL_MAGIC_WALL_ACTIVE ||
11666               element == EL_MAGIC_WALL_FULL)
11667           {
11668             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11669             TEST_DrawLevelField(x, y);
11670           }
11671           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11672                    element == EL_BD_MAGIC_WALL_FULL)
11673           {
11674             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11675             TEST_DrawLevelField(x, y);
11676           }
11677           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11678                    element == EL_DC_MAGIC_WALL_FULL)
11679           {
11680             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11681             TEST_DrawLevelField(x, y);
11682           }
11683         }
11684
11685         game.magic_wall_active = FALSE;
11686       }
11687     }
11688   }
11689
11690   if (game.light_time_left > 0)
11691   {
11692     game.light_time_left--;
11693
11694     if (game.light_time_left == 0)
11695       RedrawAllLightSwitchesAndInvisibleElements();
11696   }
11697
11698   if (game.timegate_time_left > 0)
11699   {
11700     game.timegate_time_left--;
11701
11702     if (game.timegate_time_left == 0)
11703       CloseAllOpenTimegates();
11704   }
11705
11706   if (game.lenses_time_left > 0)
11707   {
11708     game.lenses_time_left--;
11709
11710     if (game.lenses_time_left == 0)
11711       RedrawAllInvisibleElementsForLenses();
11712   }
11713
11714   if (game.magnify_time_left > 0)
11715   {
11716     game.magnify_time_left--;
11717
11718     if (game.magnify_time_left == 0)
11719       RedrawAllInvisibleElementsForMagnifier();
11720   }
11721
11722   for (i = 0; i < MAX_PLAYERS; i++)
11723   {
11724     struct PlayerInfo *player = &stored_player[i];
11725
11726     if (SHIELD_ON(player))
11727     {
11728       if (player->shield_deadly_time_left)
11729         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11730       else if (player->shield_normal_time_left)
11731         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11732     }
11733   }
11734
11735 #if USE_DELAYED_GFX_REDRAW
11736   SCAN_PLAYFIELD(x, y)
11737   {
11738     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11739     {
11740       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11741          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11742
11743       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11744         DrawLevelField(x, y);
11745
11746       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11747         DrawLevelFieldCrumbled(x, y);
11748
11749       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11750         DrawLevelFieldCrumbledNeighbours(x, y);
11751
11752       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11753         DrawTwinkleOnField(x, y);
11754     }
11755
11756     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11757   }
11758 #endif
11759
11760   DrawAllPlayers();
11761   PlayAllPlayersSound();
11762
11763   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11764   {
11765     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11766
11767     local_player->show_envelope = 0;
11768   }
11769
11770   /* use random number generator in every frame to make it less predictable */
11771   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11772     RND(1);
11773 }
11774
11775 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11776 {
11777   int min_x = x, min_y = y, max_x = x, max_y = y;
11778   int i;
11779
11780   for (i = 0; i < MAX_PLAYERS; i++)
11781   {
11782     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11783
11784     if (!stored_player[i].active || &stored_player[i] == player)
11785       continue;
11786
11787     min_x = MIN(min_x, jx);
11788     min_y = MIN(min_y, jy);
11789     max_x = MAX(max_x, jx);
11790     max_y = MAX(max_y, jy);
11791   }
11792
11793   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11794 }
11795
11796 static boolean AllPlayersInVisibleScreen()
11797 {
11798   int i;
11799
11800   for (i = 0; i < MAX_PLAYERS; i++)
11801   {
11802     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11803
11804     if (!stored_player[i].active)
11805       continue;
11806
11807     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11808       return FALSE;
11809   }
11810
11811   return TRUE;
11812 }
11813
11814 void ScrollLevel(int dx, int dy)
11815 {
11816   int scroll_offset = 2 * TILEX_VAR;
11817   int x, y;
11818
11819   BlitBitmap(drawto_field, drawto_field,
11820              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11821              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11822              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11823              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11824              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11825              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11826
11827   if (dx != 0)
11828   {
11829     x = (dx == 1 ? BX1 : BX2);
11830     for (y = BY1; y <= BY2; y++)
11831       DrawScreenField(x, y);
11832   }
11833
11834   if (dy != 0)
11835   {
11836     y = (dy == 1 ? BY1 : BY2);
11837     for (x = BX1; x <= BX2; x++)
11838       DrawScreenField(x, y);
11839   }
11840
11841   redraw_mask |= REDRAW_FIELD;
11842 }
11843
11844 static boolean canFallDown(struct PlayerInfo *player)
11845 {
11846   int jx = player->jx, jy = player->jy;
11847
11848   return (IN_LEV_FIELD(jx, jy + 1) &&
11849           (IS_FREE(jx, jy + 1) ||
11850            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11851           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11852           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11853 }
11854
11855 static boolean canPassField(int x, int y, int move_dir)
11856 {
11857   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11858   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11859   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11860   int nextx = x + dx;
11861   int nexty = y + dy;
11862   int element = Feld[x][y];
11863
11864   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11865           !CAN_MOVE(element) &&
11866           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11867           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11868           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11869 }
11870
11871 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11872 {
11873   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11874   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11875   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11876   int newx = x + dx;
11877   int newy = y + dy;
11878
11879   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11880           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11881           (IS_DIGGABLE(Feld[newx][newy]) ||
11882            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11883            canPassField(newx, newy, move_dir)));
11884 }
11885
11886 static void CheckGravityMovement(struct PlayerInfo *player)
11887 {
11888   if (player->gravity && !player->programmed_action)
11889   {
11890     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11891     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11892     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11893     int jx = player->jx, jy = player->jy;
11894     boolean player_is_moving_to_valid_field =
11895       (!player_is_snapping &&
11896        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11897         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11898     boolean player_can_fall_down = canFallDown(player);
11899
11900     if (player_can_fall_down &&
11901         !player_is_moving_to_valid_field)
11902       player->programmed_action = MV_DOWN;
11903   }
11904 }
11905
11906 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11907 {
11908   return CheckGravityMovement(player);
11909
11910   if (player->gravity && !player->programmed_action)
11911   {
11912     int jx = player->jx, jy = player->jy;
11913     boolean field_under_player_is_free =
11914       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11915     boolean player_is_standing_on_valid_field =
11916       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11917        (IS_WALKABLE(Feld[jx][jy]) &&
11918         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11919
11920     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11921       player->programmed_action = MV_DOWN;
11922   }
11923 }
11924
11925 /*
11926   MovePlayerOneStep()
11927   -----------------------------------------------------------------------------
11928   dx, dy:               direction (non-diagonal) to try to move the player to
11929   real_dx, real_dy:     direction as read from input device (can be diagonal)
11930 */
11931
11932 boolean MovePlayerOneStep(struct PlayerInfo *player,
11933                           int dx, int dy, int real_dx, int real_dy)
11934 {
11935   int jx = player->jx, jy = player->jy;
11936   int new_jx = jx + dx, new_jy = jy + dy;
11937   int can_move;
11938   boolean player_can_move = !player->cannot_move;
11939
11940   if (!player->active || (!dx && !dy))
11941     return MP_NO_ACTION;
11942
11943   player->MovDir = (dx < 0 ? MV_LEFT :
11944                     dx > 0 ? MV_RIGHT :
11945                     dy < 0 ? MV_UP :
11946                     dy > 0 ? MV_DOWN :  MV_NONE);
11947
11948   if (!IN_LEV_FIELD(new_jx, new_jy))
11949     return MP_NO_ACTION;
11950
11951   if (!player_can_move)
11952   {
11953     if (player->MovPos == 0)
11954     {
11955       player->is_moving = FALSE;
11956       player->is_digging = FALSE;
11957       player->is_collecting = FALSE;
11958       player->is_snapping = FALSE;
11959       player->is_pushing = FALSE;
11960     }
11961   }
11962
11963   if (!options.network && game.centered_player_nr == -1 &&
11964       !AllPlayersInSight(player, new_jx, new_jy))
11965     return MP_NO_ACTION;
11966
11967   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11968   if (can_move != MP_MOVING)
11969     return can_move;
11970
11971   /* check if DigField() has caused relocation of the player */
11972   if (player->jx != jx || player->jy != jy)
11973     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11974
11975   StorePlayer[jx][jy] = 0;
11976   player->last_jx = jx;
11977   player->last_jy = jy;
11978   player->jx = new_jx;
11979   player->jy = new_jy;
11980   StorePlayer[new_jx][new_jy] = player->element_nr;
11981
11982   if (player->move_delay_value_next != -1)
11983   {
11984     player->move_delay_value = player->move_delay_value_next;
11985     player->move_delay_value_next = -1;
11986   }
11987
11988   player->MovPos =
11989     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11990
11991   player->step_counter++;
11992
11993   PlayerVisit[jx][jy] = FrameCounter;
11994
11995   player->is_moving = TRUE;
11996
11997 #if 1
11998   /* should better be called in MovePlayer(), but this breaks some tapes */
11999   ScrollPlayer(player, SCROLL_INIT);
12000 #endif
12001
12002   return MP_MOVING;
12003 }
12004
12005 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12006 {
12007   int jx = player->jx, jy = player->jy;
12008   int old_jx = jx, old_jy = jy;
12009   int moved = MP_NO_ACTION;
12010
12011   if (!player->active)
12012     return FALSE;
12013
12014   if (!dx && !dy)
12015   {
12016     if (player->MovPos == 0)
12017     {
12018       player->is_moving = FALSE;
12019       player->is_digging = FALSE;
12020       player->is_collecting = FALSE;
12021       player->is_snapping = FALSE;
12022       player->is_pushing = FALSE;
12023     }
12024
12025     return FALSE;
12026   }
12027
12028   if (player->move_delay > 0)
12029     return FALSE;
12030
12031   player->move_delay = -1;              /* set to "uninitialized" value */
12032
12033   /* store if player is automatically moved to next field */
12034   player->is_auto_moving = (player->programmed_action != MV_NONE);
12035
12036   /* remove the last programmed player action */
12037   player->programmed_action = 0;
12038
12039   if (player->MovPos)
12040   {
12041     /* should only happen if pre-1.2 tape recordings are played */
12042     /* this is only for backward compatibility */
12043
12044     int original_move_delay_value = player->move_delay_value;
12045
12046 #if DEBUG
12047     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12048            tape.counter);
12049 #endif
12050
12051     /* scroll remaining steps with finest movement resolution */
12052     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12053
12054     while (player->MovPos)
12055     {
12056       ScrollPlayer(player, SCROLL_GO_ON);
12057       ScrollScreen(NULL, SCROLL_GO_ON);
12058
12059       AdvanceFrameAndPlayerCounters(player->index_nr);
12060
12061       DrawAllPlayers();
12062       BackToFront();
12063     }
12064
12065     player->move_delay_value = original_move_delay_value;
12066   }
12067
12068   player->is_active = FALSE;
12069
12070   if (player->last_move_dir & MV_HORIZONTAL)
12071   {
12072     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12073       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12074   }
12075   else
12076   {
12077     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12078       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12079   }
12080
12081   if (!moved && !player->is_active)
12082   {
12083     player->is_moving = FALSE;
12084     player->is_digging = FALSE;
12085     player->is_collecting = FALSE;
12086     player->is_snapping = FALSE;
12087     player->is_pushing = FALSE;
12088   }
12089
12090   jx = player->jx;
12091   jy = player->jy;
12092
12093   if (moved & MP_MOVING && !ScreenMovPos &&
12094       (player->index_nr == game.centered_player_nr ||
12095        game.centered_player_nr == -1))
12096   {
12097     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12098     int offset = game.scroll_delay_value;
12099
12100     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12101     {
12102       /* actual player has left the screen -- scroll in that direction */
12103       if (jx != old_jx)         /* player has moved horizontally */
12104         scroll_x += (jx - old_jx);
12105       else                      /* player has moved vertically */
12106         scroll_y += (jy - old_jy);
12107     }
12108     else
12109     {
12110       if (jx != old_jx)         /* player has moved horizontally */
12111       {
12112         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12113             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12114           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12115
12116         /* don't scroll over playfield boundaries */
12117         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12118           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12119
12120         /* don't scroll more than one field at a time */
12121         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12122
12123         /* don't scroll against the player's moving direction */
12124         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12125             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12126           scroll_x = old_scroll_x;
12127       }
12128       else                      /* player has moved vertically */
12129       {
12130         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12131             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12132           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12133
12134         /* don't scroll over playfield boundaries */
12135         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12136           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12137
12138         /* don't scroll more than one field at a time */
12139         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12140
12141         /* don't scroll against the player's moving direction */
12142         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12143             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12144           scroll_y = old_scroll_y;
12145       }
12146     }
12147
12148     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12149     {
12150       if (!options.network && game.centered_player_nr == -1 &&
12151           !AllPlayersInVisibleScreen())
12152       {
12153         scroll_x = old_scroll_x;
12154         scroll_y = old_scroll_y;
12155       }
12156       else
12157       {
12158         ScrollScreen(player, SCROLL_INIT);
12159         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12160       }
12161     }
12162   }
12163
12164   player->StepFrame = 0;
12165
12166   if (moved & MP_MOVING)
12167   {
12168     if (old_jx != jx && old_jy == jy)
12169       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12170     else if (old_jx == jx && old_jy != jy)
12171       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12172
12173     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12174
12175     player->last_move_dir = player->MovDir;
12176     player->is_moving = TRUE;
12177     player->is_snapping = FALSE;
12178     player->is_switching = FALSE;
12179     player->is_dropping = FALSE;
12180     player->is_dropping_pressed = FALSE;
12181     player->drop_pressed_delay = 0;
12182
12183 #if 0
12184     /* should better be called here than above, but this breaks some tapes */
12185     ScrollPlayer(player, SCROLL_INIT);
12186 #endif
12187   }
12188   else
12189   {
12190     CheckGravityMovementWhenNotMoving(player);
12191
12192     player->is_moving = FALSE;
12193
12194     /* at this point, the player is allowed to move, but cannot move right now
12195        (e.g. because of something blocking the way) -- ensure that the player
12196        is also allowed to move in the next frame (in old versions before 3.1.1,
12197        the player was forced to wait again for eight frames before next try) */
12198
12199     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12200       player->move_delay = 0;   /* allow direct movement in the next frame */
12201   }
12202
12203   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12204     player->move_delay = player->move_delay_value;
12205
12206   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12207   {
12208     TestIfPlayerTouchesBadThing(jx, jy);
12209     TestIfPlayerTouchesCustomElement(jx, jy);
12210   }
12211
12212   if (!player->active)
12213     RemovePlayer(player);
12214
12215   return moved;
12216 }
12217
12218 void ScrollPlayer(struct PlayerInfo *player, int mode)
12219 {
12220   int jx = player->jx, jy = player->jy;
12221   int last_jx = player->last_jx, last_jy = player->last_jy;
12222   int move_stepsize = TILEX / player->move_delay_value;
12223
12224   if (!player->active)
12225     return;
12226
12227   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12228     return;
12229
12230   if (mode == SCROLL_INIT)
12231   {
12232     player->actual_frame_counter = FrameCounter;
12233     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12234
12235     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12236         Feld[last_jx][last_jy] == EL_EMPTY)
12237     {
12238       int last_field_block_delay = 0;   /* start with no blocking at all */
12239       int block_delay_adjustment = player->block_delay_adjustment;
12240
12241       /* if player blocks last field, add delay for exactly one move */
12242       if (player->block_last_field)
12243       {
12244         last_field_block_delay += player->move_delay_value;
12245
12246         /* when blocking enabled, prevent moving up despite gravity */
12247         if (player->gravity && player->MovDir == MV_UP)
12248           block_delay_adjustment = -1;
12249       }
12250
12251       /* add block delay adjustment (also possible when not blocking) */
12252       last_field_block_delay += block_delay_adjustment;
12253
12254       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12255       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12256     }
12257
12258     if (player->MovPos != 0)    /* player has not yet reached destination */
12259       return;
12260   }
12261   else if (!FrameReached(&player->actual_frame_counter, 1))
12262     return;
12263
12264   if (player->MovPos != 0)
12265   {
12266     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12267     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12268
12269     /* before DrawPlayer() to draw correct player graphic for this case */
12270     if (player->MovPos == 0)
12271       CheckGravityMovement(player);
12272   }
12273
12274   if (player->MovPos == 0)      /* player reached destination field */
12275   {
12276     if (player->move_delay_reset_counter > 0)
12277     {
12278       player->move_delay_reset_counter--;
12279
12280       if (player->move_delay_reset_counter == 0)
12281       {
12282         /* continue with normal speed after quickly moving through gate */
12283         HALVE_PLAYER_SPEED(player);
12284
12285         /* be able to make the next move without delay */
12286         player->move_delay = 0;
12287       }
12288     }
12289
12290     player->last_jx = jx;
12291     player->last_jy = jy;
12292
12293     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12294         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12295         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12296         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12297         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12298         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12299         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12300         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12301     {
12302       DrawPlayer(player);       /* needed here only to cleanup last field */
12303       RemovePlayer(player);
12304
12305       if (local_player->friends_still_needed == 0 ||
12306           IS_SP_ELEMENT(Feld[jx][jy]))
12307         PlayerWins(player);
12308     }
12309
12310     /* this breaks one level: "machine", level 000 */
12311     {
12312       int move_direction = player->MovDir;
12313       int enter_side = MV_DIR_OPPOSITE(move_direction);
12314       int leave_side = move_direction;
12315       int old_jx = last_jx;
12316       int old_jy = last_jy;
12317       int old_element = Feld[old_jx][old_jy];
12318       int new_element = Feld[jx][jy];
12319
12320       if (IS_CUSTOM_ELEMENT(old_element))
12321         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12322                                    CE_LEFT_BY_PLAYER,
12323                                    player->index_bit, leave_side);
12324
12325       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12326                                           CE_PLAYER_LEAVES_X,
12327                                           player->index_bit, leave_side);
12328
12329       if (IS_CUSTOM_ELEMENT(new_element))
12330         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12331                                    player->index_bit, enter_side);
12332
12333       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12334                                           CE_PLAYER_ENTERS_X,
12335                                           player->index_bit, enter_side);
12336
12337       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12338                                         CE_MOVE_OF_X, move_direction);
12339     }
12340
12341     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12342     {
12343       TestIfPlayerTouchesBadThing(jx, jy);
12344       TestIfPlayerTouchesCustomElement(jx, jy);
12345
12346       /* needed because pushed element has not yet reached its destination,
12347          so it would trigger a change event at its previous field location */
12348       if (!player->is_pushing)
12349         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12350
12351       if (!player->active)
12352         RemovePlayer(player);
12353     }
12354
12355     if (!local_player->LevelSolved && level.use_step_counter)
12356     {
12357       int i;
12358
12359       TimePlayed++;
12360
12361       if (TimeLeft > 0)
12362       {
12363         TimeLeft--;
12364
12365         if (TimeLeft <= 10 && setup.time_limit)
12366           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12367
12368         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12369
12370         DisplayGameControlValues();
12371
12372         if (!TimeLeft && setup.time_limit)
12373           for (i = 0; i < MAX_PLAYERS; i++)
12374             KillPlayer(&stored_player[i]);
12375       }
12376       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12377       {
12378         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12379
12380         DisplayGameControlValues();
12381       }
12382     }
12383
12384     if (tape.single_step && tape.recording && !tape.pausing &&
12385         !player->programmed_action)
12386       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12387
12388     if (!player->programmed_action)
12389       CheckSaveEngineSnapshot(player);
12390   }
12391 }
12392
12393 void ScrollScreen(struct PlayerInfo *player, int mode)
12394 {
12395   static unsigned int screen_frame_counter = 0;
12396
12397   if (mode == SCROLL_INIT)
12398   {
12399     /* set scrolling step size according to actual player's moving speed */
12400     ScrollStepSize = TILEX / player->move_delay_value;
12401
12402     screen_frame_counter = FrameCounter;
12403     ScreenMovDir = player->MovDir;
12404     ScreenMovPos = player->MovPos;
12405     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12406     return;
12407   }
12408   else if (!FrameReached(&screen_frame_counter, 1))
12409     return;
12410
12411   if (ScreenMovPos)
12412   {
12413     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12414     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12415     redraw_mask |= REDRAW_FIELD;
12416   }
12417   else
12418     ScreenMovDir = MV_NONE;
12419 }
12420
12421 void TestIfPlayerTouchesCustomElement(int x, int y)
12422 {
12423   static int xy[4][2] =
12424   {
12425     { 0, -1 },
12426     { -1, 0 },
12427     { +1, 0 },
12428     { 0, +1 }
12429   };
12430   static int trigger_sides[4][2] =
12431   {
12432     /* center side       border side */
12433     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12434     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12435     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12436     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12437   };
12438   static int touch_dir[4] =
12439   {
12440     MV_LEFT | MV_RIGHT,
12441     MV_UP   | MV_DOWN,
12442     MV_UP   | MV_DOWN,
12443     MV_LEFT | MV_RIGHT
12444   };
12445   int center_element = Feld[x][y];      /* should always be non-moving! */
12446   int i;
12447
12448   for (i = 0; i < NUM_DIRECTIONS; i++)
12449   {
12450     int xx = x + xy[i][0];
12451     int yy = y + xy[i][1];
12452     int center_side = trigger_sides[i][0];
12453     int border_side = trigger_sides[i][1];
12454     int border_element;
12455
12456     if (!IN_LEV_FIELD(xx, yy))
12457       continue;
12458
12459     if (IS_PLAYER(x, y))                /* player found at center element */
12460     {
12461       struct PlayerInfo *player = PLAYERINFO(x, y);
12462
12463       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12464         border_element = Feld[xx][yy];          /* may be moving! */
12465       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12466         border_element = Feld[xx][yy];
12467       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12468         border_element = MovingOrBlocked2Element(xx, yy);
12469       else
12470         continue;               /* center and border element do not touch */
12471
12472       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12473                                  player->index_bit, border_side);
12474       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12475                                           CE_PLAYER_TOUCHES_X,
12476                                           player->index_bit, border_side);
12477
12478       {
12479         /* use player element that is initially defined in the level playfield,
12480            not the player element that corresponds to the runtime player number
12481            (example: a level that contains EL_PLAYER_3 as the only player would
12482            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12483         int player_element = PLAYERINFO(x, y)->initial_element;
12484
12485         CheckElementChangeBySide(xx, yy, border_element, player_element,
12486                                  CE_TOUCHING_X, border_side);
12487       }
12488     }
12489     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12490     {
12491       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12492
12493       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12494       {
12495         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12496           continue;             /* center and border element do not touch */
12497       }
12498
12499       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12500                                  player->index_bit, center_side);
12501       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12502                                           CE_PLAYER_TOUCHES_X,
12503                                           player->index_bit, center_side);
12504
12505       {
12506         /* use player element that is initially defined in the level playfield,
12507            not the player element that corresponds to the runtime player number
12508            (example: a level that contains EL_PLAYER_3 as the only player would
12509            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12510         int player_element = PLAYERINFO(xx, yy)->initial_element;
12511
12512         CheckElementChangeBySide(x, y, center_element, player_element,
12513                                  CE_TOUCHING_X, center_side);
12514       }
12515
12516       break;
12517     }
12518   }
12519 }
12520
12521 void TestIfElementTouchesCustomElement(int x, int y)
12522 {
12523   static int xy[4][2] =
12524   {
12525     { 0, -1 },
12526     { -1, 0 },
12527     { +1, 0 },
12528     { 0, +1 }
12529   };
12530   static int trigger_sides[4][2] =
12531   {
12532     /* center side      border side */
12533     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12534     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12535     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12536     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12537   };
12538   static int touch_dir[4] =
12539   {
12540     MV_LEFT | MV_RIGHT,
12541     MV_UP   | MV_DOWN,
12542     MV_UP   | MV_DOWN,
12543     MV_LEFT | MV_RIGHT
12544   };
12545   boolean change_center_element = FALSE;
12546   int center_element = Feld[x][y];      /* should always be non-moving! */
12547   int border_element_old[NUM_DIRECTIONS];
12548   int i;
12549
12550   for (i = 0; i < NUM_DIRECTIONS; i++)
12551   {
12552     int xx = x + xy[i][0];
12553     int yy = y + xy[i][1];
12554     int border_element;
12555
12556     border_element_old[i] = -1;
12557
12558     if (!IN_LEV_FIELD(xx, yy))
12559       continue;
12560
12561     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12562       border_element = Feld[xx][yy];    /* may be moving! */
12563     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12564       border_element = Feld[xx][yy];
12565     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12566       border_element = MovingOrBlocked2Element(xx, yy);
12567     else
12568       continue;                 /* center and border element do not touch */
12569
12570     border_element_old[i] = border_element;
12571   }
12572
12573   for (i = 0; i < NUM_DIRECTIONS; i++)
12574   {
12575     int xx = x + xy[i][0];
12576     int yy = y + xy[i][1];
12577     int center_side = trigger_sides[i][0];
12578     int border_element = border_element_old[i];
12579
12580     if (border_element == -1)
12581       continue;
12582
12583     /* check for change of border element */
12584     CheckElementChangeBySide(xx, yy, border_element, center_element,
12585                              CE_TOUCHING_X, center_side);
12586
12587     /* (center element cannot be player, so we dont have to check this here) */
12588   }
12589
12590   for (i = 0; i < NUM_DIRECTIONS; i++)
12591   {
12592     int xx = x + xy[i][0];
12593     int yy = y + xy[i][1];
12594     int border_side = trigger_sides[i][1];
12595     int border_element = border_element_old[i];
12596
12597     if (border_element == -1)
12598       continue;
12599
12600     /* check for change of center element (but change it only once) */
12601     if (!change_center_element)
12602       change_center_element =
12603         CheckElementChangeBySide(x, y, center_element, border_element,
12604                                  CE_TOUCHING_X, border_side);
12605
12606     if (IS_PLAYER(xx, yy))
12607     {
12608       /* use player element that is initially defined in the level playfield,
12609          not the player element that corresponds to the runtime player number
12610          (example: a level that contains EL_PLAYER_3 as the only player would
12611          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12612       int player_element = PLAYERINFO(xx, yy)->initial_element;
12613
12614       CheckElementChangeBySide(x, y, center_element, player_element,
12615                                CE_TOUCHING_X, border_side);
12616     }
12617   }
12618 }
12619
12620 void TestIfElementHitsCustomElement(int x, int y, int direction)
12621 {
12622   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12623   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12624   int hitx = x + dx, hity = y + dy;
12625   int hitting_element = Feld[x][y];
12626   int touched_element;
12627
12628   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12629     return;
12630
12631   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12632                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12633
12634   if (IN_LEV_FIELD(hitx, hity))
12635   {
12636     int opposite_direction = MV_DIR_OPPOSITE(direction);
12637     int hitting_side = direction;
12638     int touched_side = opposite_direction;
12639     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12640                           MovDir[hitx][hity] != direction ||
12641                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12642
12643     object_hit = TRUE;
12644
12645     if (object_hit)
12646     {
12647       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12648                                CE_HITTING_X, touched_side);
12649
12650       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12651                                CE_HIT_BY_X, hitting_side);
12652
12653       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12654                                CE_HIT_BY_SOMETHING, opposite_direction);
12655
12656       if (IS_PLAYER(hitx, hity))
12657       {
12658         /* use player element that is initially defined in the level playfield,
12659            not the player element that corresponds to the runtime player number
12660            (example: a level that contains EL_PLAYER_3 as the only player would
12661            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12662         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12663
12664         CheckElementChangeBySide(x, y, hitting_element, player_element,
12665                                  CE_HITTING_X, touched_side);
12666       }
12667     }
12668   }
12669
12670   /* "hitting something" is also true when hitting the playfield border */
12671   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12672                            CE_HITTING_SOMETHING, direction);
12673 }
12674
12675 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12676 {
12677   int i, kill_x = -1, kill_y = -1;
12678
12679   int bad_element = -1;
12680   static int test_xy[4][2] =
12681   {
12682     { 0, -1 },
12683     { -1, 0 },
12684     { +1, 0 },
12685     { 0, +1 }
12686   };
12687   static int test_dir[4] =
12688   {
12689     MV_UP,
12690     MV_LEFT,
12691     MV_RIGHT,
12692     MV_DOWN
12693   };
12694
12695   for (i = 0; i < NUM_DIRECTIONS; i++)
12696   {
12697     int test_x, test_y, test_move_dir, test_element;
12698
12699     test_x = good_x + test_xy[i][0];
12700     test_y = good_y + test_xy[i][1];
12701
12702     if (!IN_LEV_FIELD(test_x, test_y))
12703       continue;
12704
12705     test_move_dir =
12706       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12707
12708     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12709
12710     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12711        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12712     */
12713     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12714         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12715     {
12716       kill_x = test_x;
12717       kill_y = test_y;
12718       bad_element = test_element;
12719
12720       break;
12721     }
12722   }
12723
12724   if (kill_x != -1 || kill_y != -1)
12725   {
12726     if (IS_PLAYER(good_x, good_y))
12727     {
12728       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12729
12730       if (player->shield_deadly_time_left > 0 &&
12731           !IS_INDESTRUCTIBLE(bad_element))
12732         Bang(kill_x, kill_y);
12733       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12734         KillPlayer(player);
12735     }
12736     else
12737       Bang(good_x, good_y);
12738   }
12739 }
12740
12741 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12742 {
12743   int i, kill_x = -1, kill_y = -1;
12744   int bad_element = Feld[bad_x][bad_y];
12745   static int test_xy[4][2] =
12746   {
12747     { 0, -1 },
12748     { -1, 0 },
12749     { +1, 0 },
12750     { 0, +1 }
12751   };
12752   static int touch_dir[4] =
12753   {
12754     MV_LEFT | MV_RIGHT,
12755     MV_UP   | MV_DOWN,
12756     MV_UP   | MV_DOWN,
12757     MV_LEFT | MV_RIGHT
12758   };
12759   static int test_dir[4] =
12760   {
12761     MV_UP,
12762     MV_LEFT,
12763     MV_RIGHT,
12764     MV_DOWN
12765   };
12766
12767   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12768     return;
12769
12770   for (i = 0; i < NUM_DIRECTIONS; i++)
12771   {
12772     int test_x, test_y, test_move_dir, test_element;
12773
12774     test_x = bad_x + test_xy[i][0];
12775     test_y = bad_y + test_xy[i][1];
12776
12777     if (!IN_LEV_FIELD(test_x, test_y))
12778       continue;
12779
12780     test_move_dir =
12781       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12782
12783     test_element = Feld[test_x][test_y];
12784
12785     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12786        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12787     */
12788     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12789         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12790     {
12791       /* good thing is player or penguin that does not move away */
12792       if (IS_PLAYER(test_x, test_y))
12793       {
12794         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12795
12796         if (bad_element == EL_ROBOT && player->is_moving)
12797           continue;     /* robot does not kill player if he is moving */
12798
12799         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12800         {
12801           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12802             continue;           /* center and border element do not touch */
12803         }
12804
12805         kill_x = test_x;
12806         kill_y = test_y;
12807
12808         break;
12809       }
12810       else if (test_element == EL_PENGUIN)
12811       {
12812         kill_x = test_x;
12813         kill_y = test_y;
12814
12815         break;
12816       }
12817     }
12818   }
12819
12820   if (kill_x != -1 || kill_y != -1)
12821   {
12822     if (IS_PLAYER(kill_x, kill_y))
12823     {
12824       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12825
12826       if (player->shield_deadly_time_left > 0 &&
12827           !IS_INDESTRUCTIBLE(bad_element))
12828         Bang(bad_x, bad_y);
12829       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12830         KillPlayer(player);
12831     }
12832     else
12833       Bang(kill_x, kill_y);
12834   }
12835 }
12836
12837 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12838 {
12839   int bad_element = Feld[bad_x][bad_y];
12840   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12841   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12842   int test_x = bad_x + dx, test_y = bad_y + dy;
12843   int test_move_dir, test_element;
12844   int kill_x = -1, kill_y = -1;
12845
12846   if (!IN_LEV_FIELD(test_x, test_y))
12847     return;
12848
12849   test_move_dir =
12850     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12851
12852   test_element = Feld[test_x][test_y];
12853
12854   if (test_move_dir != bad_move_dir)
12855   {
12856     /* good thing can be player or penguin that does not move away */
12857     if (IS_PLAYER(test_x, test_y))
12858     {
12859       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12860
12861       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12862          player as being hit when he is moving towards the bad thing, because
12863          the "get hit by" condition would be lost after the player stops) */
12864       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12865         return;         /* player moves away from bad thing */
12866
12867       kill_x = test_x;
12868       kill_y = test_y;
12869     }
12870     else if (test_element == EL_PENGUIN)
12871     {
12872       kill_x = test_x;
12873       kill_y = test_y;
12874     }
12875   }
12876
12877   if (kill_x != -1 || kill_y != -1)
12878   {
12879     if (IS_PLAYER(kill_x, kill_y))
12880     {
12881       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12882
12883       if (player->shield_deadly_time_left > 0 &&
12884           !IS_INDESTRUCTIBLE(bad_element))
12885         Bang(bad_x, bad_y);
12886       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12887         KillPlayer(player);
12888     }
12889     else
12890       Bang(kill_x, kill_y);
12891   }
12892 }
12893
12894 void TestIfPlayerTouchesBadThing(int x, int y)
12895 {
12896   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12897 }
12898
12899 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12900 {
12901   TestIfGoodThingHitsBadThing(x, y, move_dir);
12902 }
12903
12904 void TestIfBadThingTouchesPlayer(int x, int y)
12905 {
12906   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12907 }
12908
12909 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12910 {
12911   TestIfBadThingHitsGoodThing(x, y, move_dir);
12912 }
12913
12914 void TestIfFriendTouchesBadThing(int x, int y)
12915 {
12916   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12917 }
12918
12919 void TestIfBadThingTouchesFriend(int x, int y)
12920 {
12921   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12922 }
12923
12924 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12925 {
12926   int i, kill_x = bad_x, kill_y = bad_y;
12927   static int xy[4][2] =
12928   {
12929     { 0, -1 },
12930     { -1, 0 },
12931     { +1, 0 },
12932     { 0, +1 }
12933   };
12934
12935   for (i = 0; i < NUM_DIRECTIONS; i++)
12936   {
12937     int x, y, element;
12938
12939     x = bad_x + xy[i][0];
12940     y = bad_y + xy[i][1];
12941     if (!IN_LEV_FIELD(x, y))
12942       continue;
12943
12944     element = Feld[x][y];
12945     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12946         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12947     {
12948       kill_x = x;
12949       kill_y = y;
12950       break;
12951     }
12952   }
12953
12954   if (kill_x != bad_x || kill_y != bad_y)
12955     Bang(bad_x, bad_y);
12956 }
12957
12958 void KillPlayer(struct PlayerInfo *player)
12959 {
12960   int jx = player->jx, jy = player->jy;
12961
12962   if (!player->active)
12963     return;
12964
12965 #if 0
12966   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12967          player->killed, player->active, player->reanimated);
12968 #endif
12969
12970   /* the following code was introduced to prevent an infinite loop when calling
12971      -> Bang()
12972      -> CheckTriggeredElementChangeExt()
12973      -> ExecuteCustomElementAction()
12974      -> KillPlayer()
12975      -> (infinitely repeating the above sequence of function calls)
12976      which occurs when killing the player while having a CE with the setting
12977      "kill player X when explosion of <player X>"; the solution using a new
12978      field "player->killed" was chosen for backwards compatibility, although
12979      clever use of the fields "player->active" etc. would probably also work */
12980 #if 1
12981   if (player->killed)
12982     return;
12983 #endif
12984
12985   player->killed = TRUE;
12986
12987   /* remove accessible field at the player's position */
12988   Feld[jx][jy] = EL_EMPTY;
12989
12990   /* deactivate shield (else Bang()/Explode() would not work right) */
12991   player->shield_normal_time_left = 0;
12992   player->shield_deadly_time_left = 0;
12993
12994 #if 0
12995   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12996          player->killed, player->active, player->reanimated);
12997 #endif
12998
12999   Bang(jx, jy);
13000
13001 #if 0
13002   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13003          player->killed, player->active, player->reanimated);
13004 #endif
13005
13006   if (player->reanimated)       /* killed player may have been reanimated */
13007     player->killed = player->reanimated = FALSE;
13008   else
13009     BuryPlayer(player);
13010 }
13011
13012 static void KillPlayerUnlessEnemyProtected(int x, int y)
13013 {
13014   if (!PLAYER_ENEMY_PROTECTED(x, y))
13015     KillPlayer(PLAYERINFO(x, y));
13016 }
13017
13018 static void KillPlayerUnlessExplosionProtected(int x, int y)
13019 {
13020   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13021     KillPlayer(PLAYERINFO(x, y));
13022 }
13023
13024 void BuryPlayer(struct PlayerInfo *player)
13025 {
13026   int jx = player->jx, jy = player->jy;
13027
13028   if (!player->active)
13029     return;
13030
13031   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13032   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13033
13034   player->GameOver = TRUE;
13035   RemovePlayer(player);
13036 }
13037
13038 void RemovePlayer(struct PlayerInfo *player)
13039 {
13040   int jx = player->jx, jy = player->jy;
13041   int i, found = FALSE;
13042
13043   player->present = FALSE;
13044   player->active = FALSE;
13045
13046   if (!ExplodeField[jx][jy])
13047     StorePlayer[jx][jy] = 0;
13048
13049   if (player->is_moving)
13050     TEST_DrawLevelField(player->last_jx, player->last_jy);
13051
13052   for (i = 0; i < MAX_PLAYERS; i++)
13053     if (stored_player[i].active)
13054       found = TRUE;
13055
13056   if (!found)
13057     AllPlayersGone = TRUE;
13058
13059   ExitX = ZX = jx;
13060   ExitY = ZY = jy;
13061 }
13062
13063 static void setFieldForSnapping(int x, int y, int element, int direction)
13064 {
13065   struct ElementInfo *ei = &element_info[element];
13066   int direction_bit = MV_DIR_TO_BIT(direction);
13067   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13068   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13069                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13070
13071   Feld[x][y] = EL_ELEMENT_SNAPPING;
13072   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13073
13074   ResetGfxAnimation(x, y);
13075
13076   GfxElement[x][y] = element;
13077   GfxAction[x][y] = action;
13078   GfxDir[x][y] = direction;
13079   GfxFrame[x][y] = -1;
13080 }
13081
13082 /*
13083   =============================================================================
13084   checkDiagonalPushing()
13085   -----------------------------------------------------------------------------
13086   check if diagonal input device direction results in pushing of object
13087   (by checking if the alternative direction is walkable, diggable, ...)
13088   =============================================================================
13089 */
13090
13091 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13092                                     int x, int y, int real_dx, int real_dy)
13093 {
13094   int jx, jy, dx, dy, xx, yy;
13095
13096   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13097     return TRUE;
13098
13099   /* diagonal direction: check alternative direction */
13100   jx = player->jx;
13101   jy = player->jy;
13102   dx = x - jx;
13103   dy = y - jy;
13104   xx = jx + (dx == 0 ? real_dx : 0);
13105   yy = jy + (dy == 0 ? real_dy : 0);
13106
13107   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13108 }
13109
13110 /*
13111   =============================================================================
13112   DigField()
13113   -----------------------------------------------------------------------------
13114   x, y:                 field next to player (non-diagonal) to try to dig to
13115   real_dx, real_dy:     direction as read from input device (can be diagonal)
13116   =============================================================================
13117 */
13118
13119 static int DigField(struct PlayerInfo *player,
13120                     int oldx, int oldy, int x, int y,
13121                     int real_dx, int real_dy, int mode)
13122 {
13123   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13124   boolean player_was_pushing = player->is_pushing;
13125   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13126   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13127   int jx = oldx, jy = oldy;
13128   int dx = x - jx, dy = y - jy;
13129   int nextx = x + dx, nexty = y + dy;
13130   int move_direction = (dx == -1 ? MV_LEFT  :
13131                         dx == +1 ? MV_RIGHT :
13132                         dy == -1 ? MV_UP    :
13133                         dy == +1 ? MV_DOWN  : MV_NONE);
13134   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13135   int dig_side = MV_DIR_OPPOSITE(move_direction);
13136   int old_element = Feld[jx][jy];
13137   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13138   int collect_count;
13139
13140   if (is_player)                /* function can also be called by EL_PENGUIN */
13141   {
13142     if (player->MovPos == 0)
13143     {
13144       player->is_digging = FALSE;
13145       player->is_collecting = FALSE;
13146     }
13147
13148     if (player->MovPos == 0)    /* last pushing move finished */
13149       player->is_pushing = FALSE;
13150
13151     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13152     {
13153       player->is_switching = FALSE;
13154       player->push_delay = -1;
13155
13156       return MP_NO_ACTION;
13157     }
13158   }
13159
13160   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13161     old_element = Back[jx][jy];
13162
13163   /* in case of element dropped at player position, check background */
13164   else if (Back[jx][jy] != EL_EMPTY &&
13165            game.engine_version >= VERSION_IDENT(2,2,0,0))
13166     old_element = Back[jx][jy];
13167
13168   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13169     return MP_NO_ACTION;        /* field has no opening in this direction */
13170
13171   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13172     return MP_NO_ACTION;        /* field has no opening in this direction */
13173
13174   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13175   {
13176     SplashAcid(x, y);
13177
13178     Feld[jx][jy] = player->artwork_element;
13179     InitMovingField(jx, jy, MV_DOWN);
13180     Store[jx][jy] = EL_ACID;
13181     ContinueMoving(jx, jy);
13182     BuryPlayer(player);
13183
13184     return MP_DONT_RUN_INTO;
13185   }
13186
13187   if (player_can_move && DONT_RUN_INTO(element))
13188   {
13189     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13190
13191     return MP_DONT_RUN_INTO;
13192   }
13193
13194   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13195     return MP_NO_ACTION;
13196
13197   collect_count = element_info[element].collect_count_initial;
13198
13199   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13200     return MP_NO_ACTION;
13201
13202   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13203     player_can_move = player_can_move_or_snap;
13204
13205   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13206       game.engine_version >= VERSION_IDENT(2,2,0,0))
13207   {
13208     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13209                                player->index_bit, dig_side);
13210     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13211                                         player->index_bit, dig_side);
13212
13213     if (element == EL_DC_LANDMINE)
13214       Bang(x, y);
13215
13216     if (Feld[x][y] != element)          /* field changed by snapping */
13217       return MP_ACTION;
13218
13219     return MP_NO_ACTION;
13220   }
13221
13222   if (player->gravity && is_player && !player->is_auto_moving &&
13223       canFallDown(player) && move_direction != MV_DOWN &&
13224       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13225     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13226
13227   if (player_can_move &&
13228       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13229   {
13230     int sound_element = SND_ELEMENT(element);
13231     int sound_action = ACTION_WALKING;
13232
13233     if (IS_RND_GATE(element))
13234     {
13235       if (!player->key[RND_GATE_NR(element)])
13236         return MP_NO_ACTION;
13237     }
13238     else if (IS_RND_GATE_GRAY(element))
13239     {
13240       if (!player->key[RND_GATE_GRAY_NR(element)])
13241         return MP_NO_ACTION;
13242     }
13243     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13244     {
13245       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13246         return MP_NO_ACTION;
13247     }
13248     else if (element == EL_EXIT_OPEN ||
13249              element == EL_EM_EXIT_OPEN ||
13250              element == EL_EM_EXIT_OPENING ||
13251              element == EL_STEEL_EXIT_OPEN ||
13252              element == EL_EM_STEEL_EXIT_OPEN ||
13253              element == EL_EM_STEEL_EXIT_OPENING ||
13254              element == EL_SP_EXIT_OPEN ||
13255              element == EL_SP_EXIT_OPENING)
13256     {
13257       sound_action = ACTION_PASSING;    /* player is passing exit */
13258     }
13259     else if (element == EL_EMPTY)
13260     {
13261       sound_action = ACTION_MOVING;             /* nothing to walk on */
13262     }
13263
13264     /* play sound from background or player, whatever is available */
13265     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13266       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13267     else
13268       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13269   }
13270   else if (player_can_move &&
13271            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13272   {
13273     if (!ACCESS_FROM(element, opposite_direction))
13274       return MP_NO_ACTION;      /* field not accessible from this direction */
13275
13276     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13277       return MP_NO_ACTION;
13278
13279     if (IS_EM_GATE(element))
13280     {
13281       if (!player->key[EM_GATE_NR(element)])
13282         return MP_NO_ACTION;
13283     }
13284     else if (IS_EM_GATE_GRAY(element))
13285     {
13286       if (!player->key[EM_GATE_GRAY_NR(element)])
13287         return MP_NO_ACTION;
13288     }
13289     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13290     {
13291       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13292         return MP_NO_ACTION;
13293     }
13294     else if (IS_EMC_GATE(element))
13295     {
13296       if (!player->key[EMC_GATE_NR(element)])
13297         return MP_NO_ACTION;
13298     }
13299     else if (IS_EMC_GATE_GRAY(element))
13300     {
13301       if (!player->key[EMC_GATE_GRAY_NR(element)])
13302         return MP_NO_ACTION;
13303     }
13304     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13305     {
13306       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13307         return MP_NO_ACTION;
13308     }
13309     else if (element == EL_DC_GATE_WHITE ||
13310              element == EL_DC_GATE_WHITE_GRAY ||
13311              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13312     {
13313       if (player->num_white_keys == 0)
13314         return MP_NO_ACTION;
13315
13316       player->num_white_keys--;
13317     }
13318     else if (IS_SP_PORT(element))
13319     {
13320       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13321           element == EL_SP_GRAVITY_PORT_RIGHT ||
13322           element == EL_SP_GRAVITY_PORT_UP ||
13323           element == EL_SP_GRAVITY_PORT_DOWN)
13324         player->gravity = !player->gravity;
13325       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13326                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13327                element == EL_SP_GRAVITY_ON_PORT_UP ||
13328                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13329         player->gravity = TRUE;
13330       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13331                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13332                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13333                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13334         player->gravity = FALSE;
13335     }
13336
13337     /* automatically move to the next field with double speed */
13338     player->programmed_action = move_direction;
13339
13340     if (player->move_delay_reset_counter == 0)
13341     {
13342       player->move_delay_reset_counter = 2;     /* two double speed steps */
13343
13344       DOUBLE_PLAYER_SPEED(player);
13345     }
13346
13347     PlayLevelSoundAction(x, y, ACTION_PASSING);
13348   }
13349   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13350   {
13351     RemoveField(x, y);
13352
13353     if (mode != DF_SNAP)
13354     {
13355       GfxElement[x][y] = GFX_ELEMENT(element);
13356       player->is_digging = TRUE;
13357     }
13358
13359     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13360
13361     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13362                                         player->index_bit, dig_side);
13363
13364     if (mode == DF_SNAP)
13365     {
13366       if (level.block_snap_field)
13367         setFieldForSnapping(x, y, element, move_direction);
13368       else
13369         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13370
13371       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13372                                           player->index_bit, dig_side);
13373     }
13374   }
13375   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13376   {
13377     RemoveField(x, y);
13378
13379     if (is_player && mode != DF_SNAP)
13380     {
13381       GfxElement[x][y] = element;
13382       player->is_collecting = TRUE;
13383     }
13384
13385     if (element == EL_SPEED_PILL)
13386     {
13387       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13388     }
13389     else if (element == EL_EXTRA_TIME && level.time > 0)
13390     {
13391       TimeLeft += level.extra_time;
13392
13393       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13394
13395       DisplayGameControlValues();
13396     }
13397     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13398     {
13399       player->shield_normal_time_left += level.shield_normal_time;
13400       if (element == EL_SHIELD_DEADLY)
13401         player->shield_deadly_time_left += level.shield_deadly_time;
13402     }
13403     else if (element == EL_DYNAMITE ||
13404              element == EL_EM_DYNAMITE ||
13405              element == EL_SP_DISK_RED)
13406     {
13407       if (player->inventory_size < MAX_INVENTORY_SIZE)
13408         player->inventory_element[player->inventory_size++] = element;
13409
13410       DrawGameDoorValues();
13411     }
13412     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13413     {
13414       player->dynabomb_count++;
13415       player->dynabombs_left++;
13416     }
13417     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13418     {
13419       player->dynabomb_size++;
13420     }
13421     else if (element == EL_DYNABOMB_INCREASE_POWER)
13422     {
13423       player->dynabomb_xl = TRUE;
13424     }
13425     else if (IS_KEY(element))
13426     {
13427       player->key[KEY_NR(element)] = TRUE;
13428
13429       DrawGameDoorValues();
13430     }
13431     else if (element == EL_DC_KEY_WHITE)
13432     {
13433       player->num_white_keys++;
13434
13435       /* display white keys? */
13436       /* DrawGameDoorValues(); */
13437     }
13438     else if (IS_ENVELOPE(element))
13439     {
13440       player->show_envelope = element;
13441     }
13442     else if (element == EL_EMC_LENSES)
13443     {
13444       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13445
13446       RedrawAllInvisibleElementsForLenses();
13447     }
13448     else if (element == EL_EMC_MAGNIFIER)
13449     {
13450       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13451
13452       RedrawAllInvisibleElementsForMagnifier();
13453     }
13454     else if (IS_DROPPABLE(element) ||
13455              IS_THROWABLE(element))     /* can be collected and dropped */
13456     {
13457       int i;
13458
13459       if (collect_count == 0)
13460         player->inventory_infinite_element = element;
13461       else
13462         for (i = 0; i < collect_count; i++)
13463           if (player->inventory_size < MAX_INVENTORY_SIZE)
13464             player->inventory_element[player->inventory_size++] = element;
13465
13466       DrawGameDoorValues();
13467     }
13468     else if (collect_count > 0)
13469     {
13470       local_player->gems_still_needed -= collect_count;
13471       if (local_player->gems_still_needed < 0)
13472         local_player->gems_still_needed = 0;
13473
13474       game.snapshot.collected_item = TRUE;
13475
13476       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13477
13478       DisplayGameControlValues();
13479     }
13480
13481     RaiseScoreElement(element);
13482     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13483
13484     if (is_player)
13485       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13486                                           player->index_bit, dig_side);
13487
13488     if (mode == DF_SNAP)
13489     {
13490       if (level.block_snap_field)
13491         setFieldForSnapping(x, y, element, move_direction);
13492       else
13493         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13494
13495       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13496                                           player->index_bit, dig_side);
13497     }
13498   }
13499   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13500   {
13501     if (mode == DF_SNAP && element != EL_BD_ROCK)
13502       return MP_NO_ACTION;
13503
13504     if (CAN_FALL(element) && dy)
13505       return MP_NO_ACTION;
13506
13507     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13508         !(element == EL_SPRING && level.use_spring_bug))
13509       return MP_NO_ACTION;
13510
13511     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13512         ((move_direction & MV_VERTICAL &&
13513           ((element_info[element].move_pattern & MV_LEFT &&
13514             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13515            (element_info[element].move_pattern & MV_RIGHT &&
13516             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13517          (move_direction & MV_HORIZONTAL &&
13518           ((element_info[element].move_pattern & MV_UP &&
13519             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13520            (element_info[element].move_pattern & MV_DOWN &&
13521             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13522       return MP_NO_ACTION;
13523
13524     /* do not push elements already moving away faster than player */
13525     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13526         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13527       return MP_NO_ACTION;
13528
13529     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13530     {
13531       if (player->push_delay_value == -1 || !player_was_pushing)
13532         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13533     }
13534     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13535     {
13536       if (player->push_delay_value == -1)
13537         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13538     }
13539     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13540     {
13541       if (!player->is_pushing)
13542         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13543     }
13544
13545     player->is_pushing = TRUE;
13546     player->is_active = TRUE;
13547
13548     if (!(IN_LEV_FIELD(nextx, nexty) &&
13549           (IS_FREE(nextx, nexty) ||
13550            (IS_SB_ELEMENT(element) &&
13551             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13552            (IS_CUSTOM_ELEMENT(element) &&
13553             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13554       return MP_NO_ACTION;
13555
13556     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13557       return MP_NO_ACTION;
13558
13559     if (player->push_delay == -1)       /* new pushing; restart delay */
13560       player->push_delay = 0;
13561
13562     if (player->push_delay < player->push_delay_value &&
13563         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13564         element != EL_SPRING && element != EL_BALLOON)
13565     {
13566       /* make sure that there is no move delay before next try to push */
13567       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13568         player->move_delay = 0;
13569
13570       return MP_NO_ACTION;
13571     }
13572
13573     if (IS_CUSTOM_ELEMENT(element) &&
13574         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13575     {
13576       if (!DigFieldByCE(nextx, nexty, element))
13577         return MP_NO_ACTION;
13578     }
13579
13580     if (IS_SB_ELEMENT(element))
13581     {
13582       if (element == EL_SOKOBAN_FIELD_FULL)
13583       {
13584         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13585         local_player->sokobanfields_still_needed++;
13586       }
13587
13588       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13589       {
13590         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13591         local_player->sokobanfields_still_needed--;
13592       }
13593
13594       Feld[x][y] = EL_SOKOBAN_OBJECT;
13595
13596       if (Back[x][y] == Back[nextx][nexty])
13597         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13598       else if (Back[x][y] != 0)
13599         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13600                                     ACTION_EMPTYING);
13601       else
13602         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13603                                     ACTION_FILLING);
13604
13605       if (local_player->sokobanfields_still_needed == 0 &&
13606           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13607       {
13608         PlayerWins(player);
13609
13610         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13611       }
13612     }
13613     else
13614       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13615
13616     InitMovingField(x, y, move_direction);
13617     GfxAction[x][y] = ACTION_PUSHING;
13618
13619     if (mode == DF_SNAP)
13620       ContinueMoving(x, y);
13621     else
13622       MovPos[x][y] = (dx != 0 ? dx : dy);
13623
13624     Pushed[x][y] = TRUE;
13625     Pushed[nextx][nexty] = TRUE;
13626
13627     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13628       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13629     else
13630       player->push_delay_value = -1;    /* get new value later */
13631
13632     /* check for element change _after_ element has been pushed */
13633     if (game.use_change_when_pushing_bug)
13634     {
13635       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13636                                  player->index_bit, dig_side);
13637       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13638                                           player->index_bit, dig_side);
13639     }
13640   }
13641   else if (IS_SWITCHABLE(element))
13642   {
13643     if (PLAYER_SWITCHING(player, x, y))
13644     {
13645       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13646                                           player->index_bit, dig_side);
13647
13648       return MP_ACTION;
13649     }
13650
13651     player->is_switching = TRUE;
13652     player->switch_x = x;
13653     player->switch_y = y;
13654
13655     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13656
13657     if (element == EL_ROBOT_WHEEL)
13658     {
13659       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13660       ZX = x;
13661       ZY = y;
13662
13663       game.robot_wheel_active = TRUE;
13664
13665       TEST_DrawLevelField(x, y);
13666     }
13667     else if (element == EL_SP_TERMINAL)
13668     {
13669       int xx, yy;
13670
13671       SCAN_PLAYFIELD(xx, yy)
13672       {
13673         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13674         {
13675           Bang(xx, yy);
13676         }
13677         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13678         {
13679           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13680
13681           ResetGfxAnimation(xx, yy);
13682           TEST_DrawLevelField(xx, yy);
13683         }
13684       }
13685     }
13686     else if (IS_BELT_SWITCH(element))
13687     {
13688       ToggleBeltSwitch(x, y);
13689     }
13690     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13691              element == EL_SWITCHGATE_SWITCH_DOWN ||
13692              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13693              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13694     {
13695       ToggleSwitchgateSwitch(x, y);
13696     }
13697     else if (element == EL_LIGHT_SWITCH ||
13698              element == EL_LIGHT_SWITCH_ACTIVE)
13699     {
13700       ToggleLightSwitch(x, y);
13701     }
13702     else if (element == EL_TIMEGATE_SWITCH ||
13703              element == EL_DC_TIMEGATE_SWITCH)
13704     {
13705       ActivateTimegateSwitch(x, y);
13706     }
13707     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13708              element == EL_BALLOON_SWITCH_RIGHT ||
13709              element == EL_BALLOON_SWITCH_UP    ||
13710              element == EL_BALLOON_SWITCH_DOWN  ||
13711              element == EL_BALLOON_SWITCH_NONE  ||
13712              element == EL_BALLOON_SWITCH_ANY)
13713     {
13714       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13715                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13716                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13717                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13718                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13719                              move_direction);
13720     }
13721     else if (element == EL_LAMP)
13722     {
13723       Feld[x][y] = EL_LAMP_ACTIVE;
13724       local_player->lights_still_needed--;
13725
13726       ResetGfxAnimation(x, y);
13727       TEST_DrawLevelField(x, y);
13728     }
13729     else if (element == EL_TIME_ORB_FULL)
13730     {
13731       Feld[x][y] = EL_TIME_ORB_EMPTY;
13732
13733       if (level.time > 0 || level.use_time_orb_bug)
13734       {
13735         TimeLeft += level.time_orb_time;
13736         game.no_time_limit = FALSE;
13737
13738         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13739
13740         DisplayGameControlValues();
13741       }
13742
13743       ResetGfxAnimation(x, y);
13744       TEST_DrawLevelField(x, y);
13745     }
13746     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13747              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13748     {
13749       int xx, yy;
13750
13751       game.ball_state = !game.ball_state;
13752
13753       SCAN_PLAYFIELD(xx, yy)
13754       {
13755         int e = Feld[xx][yy];
13756
13757         if (game.ball_state)
13758         {
13759           if (e == EL_EMC_MAGIC_BALL)
13760             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13761           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13762             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13763         }
13764         else
13765         {
13766           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13767             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13768           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13769             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13770         }
13771       }
13772     }
13773
13774     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13775                                         player->index_bit, dig_side);
13776
13777     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13778                                         player->index_bit, dig_side);
13779
13780     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13781                                         player->index_bit, dig_side);
13782
13783     return MP_ACTION;
13784   }
13785   else
13786   {
13787     if (!PLAYER_SWITCHING(player, x, y))
13788     {
13789       player->is_switching = TRUE;
13790       player->switch_x = x;
13791       player->switch_y = y;
13792
13793       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13794                                  player->index_bit, dig_side);
13795       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13796                                           player->index_bit, dig_side);
13797
13798       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13799                                  player->index_bit, dig_side);
13800       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13801                                           player->index_bit, dig_side);
13802     }
13803
13804     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13805                                player->index_bit, dig_side);
13806     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13807                                         player->index_bit, dig_side);
13808
13809     return MP_NO_ACTION;
13810   }
13811
13812   player->push_delay = -1;
13813
13814   if (is_player)                /* function can also be called by EL_PENGUIN */
13815   {
13816     if (Feld[x][y] != element)          /* really digged/collected something */
13817     {
13818       player->is_collecting = !player->is_digging;
13819       player->is_active = TRUE;
13820     }
13821   }
13822
13823   return MP_MOVING;
13824 }
13825
13826 static boolean DigFieldByCE(int x, int y, int digging_element)
13827 {
13828   int element = Feld[x][y];
13829
13830   if (!IS_FREE(x, y))
13831   {
13832     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13833                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13834                   ACTION_BREAKING);
13835
13836     /* no element can dig solid indestructible elements */
13837     if (IS_INDESTRUCTIBLE(element) &&
13838         !IS_DIGGABLE(element) &&
13839         !IS_COLLECTIBLE(element))
13840       return FALSE;
13841
13842     if (AmoebaNr[x][y] &&
13843         (element == EL_AMOEBA_FULL ||
13844          element == EL_BD_AMOEBA ||
13845          element == EL_AMOEBA_GROWING))
13846     {
13847       AmoebaCnt[AmoebaNr[x][y]]--;
13848       AmoebaCnt2[AmoebaNr[x][y]]--;
13849     }
13850
13851     if (IS_MOVING(x, y))
13852       RemoveMovingField(x, y);
13853     else
13854     {
13855       RemoveField(x, y);
13856       TEST_DrawLevelField(x, y);
13857     }
13858
13859     /* if digged element was about to explode, prevent the explosion */
13860     ExplodeField[x][y] = EX_TYPE_NONE;
13861
13862     PlayLevelSoundAction(x, y, action);
13863   }
13864
13865   Store[x][y] = EL_EMPTY;
13866
13867   /* this makes it possible to leave the removed element again */
13868   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13869     Store[x][y] = element;
13870
13871   return TRUE;
13872 }
13873
13874 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13875 {
13876   int jx = player->jx, jy = player->jy;
13877   int x = jx + dx, y = jy + dy;
13878   int snap_direction = (dx == -1 ? MV_LEFT  :
13879                         dx == +1 ? MV_RIGHT :
13880                         dy == -1 ? MV_UP    :
13881                         dy == +1 ? MV_DOWN  : MV_NONE);
13882   boolean can_continue_snapping = (level.continuous_snapping &&
13883                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13884
13885   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13886     return FALSE;
13887
13888   if (!player->active || !IN_LEV_FIELD(x, y))
13889     return FALSE;
13890
13891   if (dx && dy)
13892     return FALSE;
13893
13894   if (!dx && !dy)
13895   {
13896     if (player->MovPos == 0)
13897       player->is_pushing = FALSE;
13898
13899     player->is_snapping = FALSE;
13900
13901     if (player->MovPos == 0)
13902     {
13903       player->is_moving = FALSE;
13904       player->is_digging = FALSE;
13905       player->is_collecting = FALSE;
13906     }
13907
13908     return FALSE;
13909   }
13910
13911   /* prevent snapping with already pressed snap key when not allowed */
13912   if (player->is_snapping && !can_continue_snapping)
13913     return FALSE;
13914
13915   player->MovDir = snap_direction;
13916
13917   if (player->MovPos == 0)
13918   {
13919     player->is_moving = FALSE;
13920     player->is_digging = FALSE;
13921     player->is_collecting = FALSE;
13922   }
13923
13924   player->is_dropping = FALSE;
13925   player->is_dropping_pressed = FALSE;
13926   player->drop_pressed_delay = 0;
13927
13928   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13929     return FALSE;
13930
13931   player->is_snapping = TRUE;
13932   player->is_active = TRUE;
13933
13934   if (player->MovPos == 0)
13935   {
13936     player->is_moving = FALSE;
13937     player->is_digging = FALSE;
13938     player->is_collecting = FALSE;
13939   }
13940
13941   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13942     TEST_DrawLevelField(player->last_jx, player->last_jy);
13943
13944   TEST_DrawLevelField(x, y);
13945
13946   return TRUE;
13947 }
13948
13949 static boolean DropElement(struct PlayerInfo *player)
13950 {
13951   int old_element, new_element;
13952   int dropx = player->jx, dropy = player->jy;
13953   int drop_direction = player->MovDir;
13954   int drop_side = drop_direction;
13955   int drop_element = get_next_dropped_element(player);
13956
13957   player->is_dropping_pressed = TRUE;
13958
13959   /* do not drop an element on top of another element; when holding drop key
13960      pressed without moving, dropped element must move away before the next
13961      element can be dropped (this is especially important if the next element
13962      is dynamite, which can be placed on background for historical reasons) */
13963   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13964     return MP_ACTION;
13965
13966   if (IS_THROWABLE(drop_element))
13967   {
13968     dropx += GET_DX_FROM_DIR(drop_direction);
13969     dropy += GET_DY_FROM_DIR(drop_direction);
13970
13971     if (!IN_LEV_FIELD(dropx, dropy))
13972       return FALSE;
13973   }
13974
13975   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13976   new_element = drop_element;           /* default: no change when dropping */
13977
13978   /* check if player is active, not moving and ready to drop */
13979   if (!player->active || player->MovPos || player->drop_delay > 0)
13980     return FALSE;
13981
13982   /* check if player has anything that can be dropped */
13983   if (new_element == EL_UNDEFINED)
13984     return FALSE;
13985
13986   /* check if drop key was pressed long enough for EM style dynamite */
13987   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13988     return FALSE;
13989
13990   /* check if anything can be dropped at the current position */
13991   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13992     return FALSE;
13993
13994   /* collected custom elements can only be dropped on empty fields */
13995   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13996     return FALSE;
13997
13998   if (old_element != EL_EMPTY)
13999     Back[dropx][dropy] = old_element;   /* store old element on this field */
14000
14001   ResetGfxAnimation(dropx, dropy);
14002   ResetRandomAnimationValue(dropx, dropy);
14003
14004   if (player->inventory_size > 0 ||
14005       player->inventory_infinite_element != EL_UNDEFINED)
14006   {
14007     if (player->inventory_size > 0)
14008     {
14009       player->inventory_size--;
14010
14011       DrawGameDoorValues();
14012
14013       if (new_element == EL_DYNAMITE)
14014         new_element = EL_DYNAMITE_ACTIVE;
14015       else if (new_element == EL_EM_DYNAMITE)
14016         new_element = EL_EM_DYNAMITE_ACTIVE;
14017       else if (new_element == EL_SP_DISK_RED)
14018         new_element = EL_SP_DISK_RED_ACTIVE;
14019     }
14020
14021     Feld[dropx][dropy] = new_element;
14022
14023     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14024       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14025                           el2img(Feld[dropx][dropy]), 0);
14026
14027     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14028
14029     /* needed if previous element just changed to "empty" in the last frame */
14030     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14031
14032     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14033                                player->index_bit, drop_side);
14034     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14035                                         CE_PLAYER_DROPS_X,
14036                                         player->index_bit, drop_side);
14037
14038     TestIfElementTouchesCustomElement(dropx, dropy);
14039   }
14040   else          /* player is dropping a dyna bomb */
14041   {
14042     player->dynabombs_left--;
14043
14044     Feld[dropx][dropy] = new_element;
14045
14046     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14047       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14048                           el2img(Feld[dropx][dropy]), 0);
14049
14050     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14051   }
14052
14053   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14054     InitField_WithBug1(dropx, dropy, FALSE);
14055
14056   new_element = Feld[dropx][dropy];     /* element might have changed */
14057
14058   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14059       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14060   {
14061     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14062       MovDir[dropx][dropy] = drop_direction;
14063
14064     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14065
14066     /* do not cause impact style collision by dropping elements that can fall */
14067     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14068   }
14069
14070   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14071   player->is_dropping = TRUE;
14072
14073   player->drop_pressed_delay = 0;
14074   player->is_dropping_pressed = FALSE;
14075
14076   player->drop_x = dropx;
14077   player->drop_y = dropy;
14078
14079   return TRUE;
14080 }
14081
14082 /* ------------------------------------------------------------------------- */
14083 /* game sound playing functions                                              */
14084 /* ------------------------------------------------------------------------- */
14085
14086 static int *loop_sound_frame = NULL;
14087 static int *loop_sound_volume = NULL;
14088
14089 void InitPlayLevelSound()
14090 {
14091   int num_sounds = getSoundListSize();
14092
14093   checked_free(loop_sound_frame);
14094   checked_free(loop_sound_volume);
14095
14096   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14097   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14098 }
14099
14100 static void PlayLevelSound(int x, int y, int nr)
14101 {
14102   int sx = SCREENX(x), sy = SCREENY(y);
14103   int volume, stereo_position;
14104   int max_distance = 8;
14105   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14106
14107   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14108       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14109     return;
14110
14111   if (!IN_LEV_FIELD(x, y) ||
14112       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14113       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14114     return;
14115
14116   volume = SOUND_MAX_VOLUME;
14117
14118   if (!IN_SCR_FIELD(sx, sy))
14119   {
14120     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14121     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14122
14123     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14124   }
14125
14126   stereo_position = (SOUND_MAX_LEFT +
14127                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14128                      (SCR_FIELDX + 2 * max_distance));
14129
14130   if (IS_LOOP_SOUND(nr))
14131   {
14132     /* This assures that quieter loop sounds do not overwrite louder ones,
14133        while restarting sound volume comparison with each new game frame. */
14134
14135     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14136       return;
14137
14138     loop_sound_volume[nr] = volume;
14139     loop_sound_frame[nr] = FrameCounter;
14140   }
14141
14142   PlaySoundExt(nr, volume, stereo_position, type);
14143 }
14144
14145 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14146 {
14147   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14148                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14149                  y < LEVELY(BY1) ? LEVELY(BY1) :
14150                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14151                  sound_action);
14152 }
14153
14154 static void PlayLevelSoundAction(int x, int y, int action)
14155 {
14156   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14157 }
14158
14159 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14160 {
14161   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14162
14163   if (sound_effect != SND_UNDEFINED)
14164     PlayLevelSound(x, y, sound_effect);
14165 }
14166
14167 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14168                                               int action)
14169 {
14170   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14171
14172   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14173     PlayLevelSound(x, y, sound_effect);
14174 }
14175
14176 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14177 {
14178   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14179
14180   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14181     PlayLevelSound(x, y, sound_effect);
14182 }
14183
14184 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14185 {
14186   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14187
14188   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14189     StopSound(sound_effect);
14190 }
14191
14192 static void PlayLevelMusic()
14193 {
14194   if (levelset.music[level_nr] != MUS_UNDEFINED)
14195     PlayMusic(levelset.music[level_nr]);        /* from config file */
14196   else
14197     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14198 }
14199
14200 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14201 {
14202   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14203   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14204   int x = xx - 1 - offset;
14205   int y = yy - 1 - offset;
14206
14207   switch (sample)
14208   {
14209     case SAMPLE_blank:
14210       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14211       break;
14212
14213     case SAMPLE_roll:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14215       break;
14216
14217     case SAMPLE_stone:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14219       break;
14220
14221     case SAMPLE_nut:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14223       break;
14224
14225     case SAMPLE_crack:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14227       break;
14228
14229     case SAMPLE_bug:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14231       break;
14232
14233     case SAMPLE_tank:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14235       break;
14236
14237     case SAMPLE_android_clone:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14239       break;
14240
14241     case SAMPLE_android_move:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14243       break;
14244
14245     case SAMPLE_spring:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14247       break;
14248
14249     case SAMPLE_slurp:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14251       break;
14252
14253     case SAMPLE_eater:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14255       break;
14256
14257     case SAMPLE_eater_eat:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14259       break;
14260
14261     case SAMPLE_alien:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14263       break;
14264
14265     case SAMPLE_collect:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14267       break;
14268
14269     case SAMPLE_diamond:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14271       break;
14272
14273     case SAMPLE_squash:
14274       /* !!! CHECK THIS !!! */
14275 #if 1
14276       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14277 #else
14278       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14279 #endif
14280       break;
14281
14282     case SAMPLE_wonderfall:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14284       break;
14285
14286     case SAMPLE_drip:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14288       break;
14289
14290     case SAMPLE_push:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14292       break;
14293
14294     case SAMPLE_dirt:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14296       break;
14297
14298     case SAMPLE_acid:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14300       break;
14301
14302     case SAMPLE_ball:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14304       break;
14305
14306     case SAMPLE_grow:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14308       break;
14309
14310     case SAMPLE_wonder:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14312       break;
14313
14314     case SAMPLE_door:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14316       break;
14317
14318     case SAMPLE_exit_open:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14320       break;
14321
14322     case SAMPLE_exit_leave:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14324       break;
14325
14326     case SAMPLE_dynamite:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14328       break;
14329
14330     case SAMPLE_tick:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14332       break;
14333
14334     case SAMPLE_press:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14336       break;
14337
14338     case SAMPLE_wheel:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14340       break;
14341
14342     case SAMPLE_boom:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14344       break;
14345
14346     case SAMPLE_die:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14348       break;
14349
14350     case SAMPLE_time:
14351       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14352       break;
14353
14354     default:
14355       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14356       break;
14357   }
14358 }
14359
14360 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14361 {
14362   int element = map_element_SP_to_RND(element_sp);
14363   int action = map_action_SP_to_RND(action_sp);
14364   int offset = (setup.sp_show_border_elements ? 0 : 1);
14365   int x = xx - offset;
14366   int y = yy - offset;
14367
14368   PlayLevelSoundElementAction(x, y, element, action);
14369 }
14370
14371 void RaiseScore(int value)
14372 {
14373   local_player->score += value;
14374
14375   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14376
14377   DisplayGameControlValues();
14378 }
14379
14380 void RaiseScoreElement(int element)
14381 {
14382   switch (element)
14383   {
14384     case EL_EMERALD:
14385     case EL_BD_DIAMOND:
14386     case EL_EMERALD_YELLOW:
14387     case EL_EMERALD_RED:
14388     case EL_EMERALD_PURPLE:
14389     case EL_SP_INFOTRON:
14390       RaiseScore(level.score[SC_EMERALD]);
14391       break;
14392     case EL_DIAMOND:
14393       RaiseScore(level.score[SC_DIAMOND]);
14394       break;
14395     case EL_CRYSTAL:
14396       RaiseScore(level.score[SC_CRYSTAL]);
14397       break;
14398     case EL_PEARL:
14399       RaiseScore(level.score[SC_PEARL]);
14400       break;
14401     case EL_BUG:
14402     case EL_BD_BUTTERFLY:
14403     case EL_SP_ELECTRON:
14404       RaiseScore(level.score[SC_BUG]);
14405       break;
14406     case EL_SPACESHIP:
14407     case EL_BD_FIREFLY:
14408     case EL_SP_SNIKSNAK:
14409       RaiseScore(level.score[SC_SPACESHIP]);
14410       break;
14411     case EL_YAMYAM:
14412     case EL_DARK_YAMYAM:
14413       RaiseScore(level.score[SC_YAMYAM]);
14414       break;
14415     case EL_ROBOT:
14416       RaiseScore(level.score[SC_ROBOT]);
14417       break;
14418     case EL_PACMAN:
14419       RaiseScore(level.score[SC_PACMAN]);
14420       break;
14421     case EL_NUT:
14422       RaiseScore(level.score[SC_NUT]);
14423       break;
14424     case EL_DYNAMITE:
14425     case EL_EM_DYNAMITE:
14426     case EL_SP_DISK_RED:
14427     case EL_DYNABOMB_INCREASE_NUMBER:
14428     case EL_DYNABOMB_INCREASE_SIZE:
14429     case EL_DYNABOMB_INCREASE_POWER:
14430       RaiseScore(level.score[SC_DYNAMITE]);
14431       break;
14432     case EL_SHIELD_NORMAL:
14433     case EL_SHIELD_DEADLY:
14434       RaiseScore(level.score[SC_SHIELD]);
14435       break;
14436     case EL_EXTRA_TIME:
14437       RaiseScore(level.extra_time_score);
14438       break;
14439     case EL_KEY_1:
14440     case EL_KEY_2:
14441     case EL_KEY_3:
14442     case EL_KEY_4:
14443     case EL_EM_KEY_1:
14444     case EL_EM_KEY_2:
14445     case EL_EM_KEY_3:
14446     case EL_EM_KEY_4:
14447     case EL_EMC_KEY_5:
14448     case EL_EMC_KEY_6:
14449     case EL_EMC_KEY_7:
14450     case EL_EMC_KEY_8:
14451     case EL_DC_KEY_WHITE:
14452       RaiseScore(level.score[SC_KEY]);
14453       break;
14454     default:
14455       RaiseScore(element_info[element].collect_score);
14456       break;
14457   }
14458 }
14459
14460 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14461 {
14462   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14463   {
14464     /* closing door required in case of envelope style request dialogs */
14465     if (!skip_request)
14466       CloseDoor(DOOR_CLOSE_1);
14467
14468 #if defined(NETWORK_AVALIABLE)
14469     if (options.network)
14470       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14471     else
14472 #endif
14473     {
14474       if (quick_quit)
14475         FadeSkipNextFadeIn();
14476
14477       SetGameStatus(GAME_MODE_MAIN);
14478
14479       DrawMainMenu();
14480     }
14481   }
14482   else          /* continue playing the game */
14483   {
14484     if (tape.playing && tape.deactivate_display)
14485       TapeDeactivateDisplayOff(TRUE);
14486
14487     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14488
14489     if (tape.playing && tape.deactivate_display)
14490       TapeDeactivateDisplayOn();
14491   }
14492 }
14493
14494 void RequestQuitGame(boolean ask_if_really_quit)
14495 {
14496   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14497   boolean skip_request = AllPlayersGone || quick_quit;
14498
14499   RequestQuitGameExt(skip_request, quick_quit,
14500                      "Do you really want to quit the game?");
14501 }
14502
14503
14504 /* ------------------------------------------------------------------------- */
14505 /* random generator functions                                                */
14506 /* ------------------------------------------------------------------------- */
14507
14508 unsigned int InitEngineRandom_RND(int seed)
14509 {
14510   game.num_random_calls = 0;
14511
14512   return InitEngineRandom(seed);
14513 }
14514
14515 unsigned int RND(int max)
14516 {
14517   if (max > 0)
14518   {
14519     game.num_random_calls++;
14520
14521     return GetEngineRandom(max);
14522   }
14523
14524   return 0;
14525 }
14526
14527
14528 /* ------------------------------------------------------------------------- */
14529 /* game engine snapshot handling functions                                   */
14530 /* ------------------------------------------------------------------------- */
14531
14532 struct EngineSnapshotInfo
14533 {
14534   /* runtime values for custom element collect score */
14535   int collect_score[NUM_CUSTOM_ELEMENTS];
14536
14537   /* runtime values for group element choice position */
14538   int choice_pos[NUM_GROUP_ELEMENTS];
14539
14540   /* runtime values for belt position animations */
14541   int belt_graphic[4][NUM_BELT_PARTS];
14542   int belt_anim_mode[4][NUM_BELT_PARTS];
14543 };
14544
14545 static struct EngineSnapshotInfo engine_snapshot_rnd;
14546 static char *snapshot_level_identifier = NULL;
14547 static int snapshot_level_nr = -1;
14548
14549 static void SaveEngineSnapshotValues_RND()
14550 {
14551   static int belt_base_active_element[4] =
14552   {
14553     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14554     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14555     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14556     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14557   };
14558   int i, j;
14559
14560   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14561   {
14562     int element = EL_CUSTOM_START + i;
14563
14564     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14565   }
14566
14567   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14568   {
14569     int element = EL_GROUP_START + i;
14570
14571     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14572   }
14573
14574   for (i = 0; i < 4; i++)
14575   {
14576     for (j = 0; j < NUM_BELT_PARTS; j++)
14577     {
14578       int element = belt_base_active_element[i] + j;
14579       int graphic = el2img(element);
14580       int anim_mode = graphic_info[graphic].anim_mode;
14581
14582       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14583       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14584     }
14585   }
14586 }
14587
14588 static void LoadEngineSnapshotValues_RND()
14589 {
14590   unsigned int num_random_calls = game.num_random_calls;
14591   int i, j;
14592
14593   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14594   {
14595     int element = EL_CUSTOM_START + i;
14596
14597     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14598   }
14599
14600   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14601   {
14602     int element = EL_GROUP_START + i;
14603
14604     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14605   }
14606
14607   for (i = 0; i < 4; i++)
14608   {
14609     for (j = 0; j < NUM_BELT_PARTS; j++)
14610     {
14611       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14612       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14613
14614       graphic_info[graphic].anim_mode = anim_mode;
14615     }
14616   }
14617
14618   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14619   {
14620     InitRND(tape.random_seed);
14621     for (i = 0; i < num_random_calls; i++)
14622       RND(1);
14623   }
14624
14625   if (game.num_random_calls != num_random_calls)
14626   {
14627     Error(ERR_INFO, "number of random calls out of sync");
14628     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14629     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14630     Error(ERR_EXIT, "this should not happen -- please debug");
14631   }
14632 }
14633
14634 void FreeEngineSnapshotSingle()
14635 {
14636   FreeSnapshotSingle();
14637
14638   setString(&snapshot_level_identifier, NULL);
14639   snapshot_level_nr = -1;
14640 }
14641
14642 void FreeEngineSnapshotList()
14643 {
14644   FreeSnapshotList();
14645 }
14646
14647 ListNode *SaveEngineSnapshotBuffers()
14648 {
14649   ListNode *buffers = NULL;
14650
14651   /* copy some special values to a structure better suited for the snapshot */
14652
14653   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14654     SaveEngineSnapshotValues_RND();
14655   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14656     SaveEngineSnapshotValues_EM();
14657   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14658     SaveEngineSnapshotValues_SP(&buffers);
14659
14660   /* save values stored in special snapshot structure */
14661
14662   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14663     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14664   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14665     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14666   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14667     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14668
14669   /* save further RND engine values */
14670
14671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14674
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14679
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14685
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14689
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14691
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14693
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14696
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14715
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14718
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14722
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14725
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14731
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14734
14735 #if 0
14736   ListNode *node = engine_snapshot_list_rnd;
14737   int num_bytes = 0;
14738
14739   while (node != NULL)
14740   {
14741     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14742
14743     node = node->next;
14744   }
14745
14746   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14747 #endif
14748
14749   return buffers;
14750 }
14751
14752 void SaveEngineSnapshotSingle()
14753 {
14754   ListNode *buffers = SaveEngineSnapshotBuffers();
14755
14756   /* finally save all snapshot buffers to single snapshot */
14757   SaveSnapshotSingle(buffers);
14758
14759   /* save level identification information */
14760   setString(&snapshot_level_identifier, leveldir_current->identifier);
14761   snapshot_level_nr = level_nr;
14762 }
14763
14764 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14765 {
14766   boolean save_snapshot =
14767     (initial_snapshot ||
14768      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14769      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14770       game.snapshot.changed_action) ||
14771      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14772       game.snapshot.collected_item));
14773
14774   game.snapshot.changed_action = FALSE;
14775   game.snapshot.collected_item = FALSE;
14776
14777   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14778       tape.quick_resume ||
14779       !save_snapshot)
14780     return FALSE;
14781
14782   ListNode *buffers = SaveEngineSnapshotBuffers();
14783
14784   /* finally save all snapshot buffers to snapshot list */
14785   SaveSnapshotToList(buffers);
14786
14787   return TRUE;
14788 }
14789
14790 boolean SaveEngineSnapshotToList()
14791 {
14792   return SaveEngineSnapshotToListExt(FALSE);
14793 }
14794
14795 void SaveEngineSnapshotToListInitial()
14796 {
14797   FreeEngineSnapshotList();
14798
14799   SaveEngineSnapshotToListExt(TRUE);
14800 }
14801
14802 void LoadEngineSnapshotValues()
14803 {
14804   /* restore special values from snapshot structure */
14805
14806   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14807     LoadEngineSnapshotValues_RND();
14808   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14809     LoadEngineSnapshotValues_EM();
14810   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14811     LoadEngineSnapshotValues_SP();
14812 }
14813
14814 void LoadEngineSnapshotSingle()
14815 {
14816   LoadSnapshotSingle();
14817
14818   LoadEngineSnapshotValues();
14819 }
14820
14821 void LoadEngineSnapshot_Undo(int steps)
14822 {
14823   LoadSnapshotFromList_Older(steps);
14824
14825   LoadEngineSnapshotValues();
14826 }
14827
14828 void LoadEngineSnapshot_Redo(int steps)
14829 {
14830   LoadSnapshotFromList_Newer(steps);
14831
14832   LoadEngineSnapshotValues();
14833 }
14834
14835 boolean CheckEngineSnapshotSingle()
14836 {
14837   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14838           snapshot_level_nr == level_nr);
14839 }
14840
14841 boolean CheckEngineSnapshotList()
14842 {
14843   return CheckSnapshotList();
14844 }
14845
14846
14847 /* ---------- new game button stuff ---------------------------------------- */
14848
14849 static struct
14850 {
14851   int graphic;
14852   struct XY *pos;
14853   int gadget_id;
14854   char *infotext;
14855 } gamebutton_info[NUM_GAME_BUTTONS] =
14856 {
14857   {
14858     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14859     GAME_CTRL_ID_STOP,                  "stop game"
14860   },
14861   {
14862     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14863     GAME_CTRL_ID_PAUSE,                 "pause game"
14864   },
14865   {
14866     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14867     GAME_CTRL_ID_PLAY,                  "play game"
14868   },
14869   {
14870     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14871     GAME_CTRL_ID_UNDO,                  "undo step"
14872   },
14873   {
14874     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14875     GAME_CTRL_ID_REDO,                  "redo step"
14876   },
14877   {
14878     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14879     GAME_CTRL_ID_SAVE,                  "save game"
14880   },
14881   {
14882     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14883     GAME_CTRL_ID_PAUSE2,                "pause game"
14884   },
14885   {
14886     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14887     GAME_CTRL_ID_LOAD,                  "load game"
14888   },
14889   {
14890     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14891     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14892   },
14893   {
14894     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14895     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14896   },
14897   {
14898     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14899     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14900   }
14901 };
14902
14903 void CreateGameButtons()
14904 {
14905   int i;
14906
14907   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14908   {
14909     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14910     struct XY *pos = gamebutton_info[i].pos;
14911     struct GadgetInfo *gi;
14912     int button_type;
14913     boolean checked;
14914     unsigned int event_mask;
14915     int base_x = (tape.show_game_buttons ? VX : DX);
14916     int base_y = (tape.show_game_buttons ? VY : DY);
14917     int gd_x   = gfx->src_x;
14918     int gd_y   = gfx->src_y;
14919     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14920     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14921     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14922     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14923     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14924     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14925     int id = i;
14926
14927     if (gfx->bitmap == NULL)
14928     {
14929       game_gadget[id] = NULL;
14930
14931       continue;
14932     }
14933
14934     if (id == GAME_CTRL_ID_STOP ||
14935         id == GAME_CTRL_ID_PLAY ||
14936         id == GAME_CTRL_ID_SAVE ||
14937         id == GAME_CTRL_ID_LOAD)
14938     {
14939       button_type = GD_TYPE_NORMAL_BUTTON;
14940       checked = FALSE;
14941       event_mask = GD_EVENT_RELEASED;
14942     }
14943     else if (id == GAME_CTRL_ID_UNDO ||
14944              id == GAME_CTRL_ID_REDO)
14945     {
14946       button_type = GD_TYPE_NORMAL_BUTTON;
14947       checked = FALSE;
14948       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14949     }
14950     else
14951     {
14952       button_type = GD_TYPE_CHECK_BUTTON;
14953       checked =
14954         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14955          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14956          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14957       event_mask = GD_EVENT_PRESSED;
14958     }
14959
14960     gi = CreateGadget(GDI_CUSTOM_ID, id,
14961                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14962                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14963                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14964                       GDI_WIDTH, gfx->width,
14965                       GDI_HEIGHT, gfx->height,
14966                       GDI_TYPE, button_type,
14967                       GDI_STATE, GD_BUTTON_UNPRESSED,
14968                       GDI_CHECKED, checked,
14969                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14970                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14971                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14972                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14973                       GDI_DIRECT_DRAW, FALSE,
14974                       GDI_EVENT_MASK, event_mask,
14975                       GDI_CALLBACK_ACTION, HandleGameButtons,
14976                       GDI_END);
14977
14978     if (gi == NULL)
14979       Error(ERR_EXIT, "cannot create gadget");
14980
14981     game_gadget[id] = gi;
14982   }
14983 }
14984
14985 void FreeGameButtons()
14986 {
14987   int i;
14988
14989   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14990     FreeGadget(game_gadget[i]);
14991 }
14992
14993 static void MapGameButtonsAtSamePosition(int id)
14994 {
14995   int i;
14996
14997   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14998     if (i != id &&
14999         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15000         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15001       MapGadget(game_gadget[i]);
15002 }
15003
15004 static void UnmapGameButtonsAtSamePosition(int id)
15005 {
15006   int i;
15007
15008   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15009     if (i != id &&
15010         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15011         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15012       UnmapGadget(game_gadget[i]);
15013 }
15014
15015 void MapUndoRedoButtons()
15016 {
15017   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15018   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15019
15020   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15021   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15022 }
15023
15024 void UnmapUndoRedoButtons()
15025 {
15026   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15027   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15028
15029   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15030   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15031 }
15032
15033 void MapGameButtons()
15034 {
15035   int i;
15036
15037   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15038     if (i != GAME_CTRL_ID_UNDO &&
15039         i != GAME_CTRL_ID_REDO)
15040       MapGadget(game_gadget[i]);
15041
15042   if (setup.show_snapshot_buttons)
15043   {
15044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15045     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15046     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15047   }
15048   else
15049   {
15050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15051     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15052     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15053   }
15054
15055   RedrawGameButtons();
15056 }
15057
15058 void UnmapGameButtons()
15059 {
15060   int i;
15061
15062   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15063     UnmapGadget(game_gadget[i]);
15064 }
15065
15066 void RedrawGameButtons()
15067 {
15068   int i;
15069
15070   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15071     RedrawGadget(game_gadget[i]);
15072
15073   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15074   redraw_mask &= ~REDRAW_ALL;
15075 }
15076
15077 void GameUndoRedoExt()
15078 {
15079   ClearPlayerAction();
15080
15081   tape.pausing = TRUE;
15082
15083   RedrawPlayfield();
15084   UpdateAndDisplayGameControlValues();
15085
15086   DrawCompleteVideoDisplay();
15087   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15088   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15089   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15090                     VIDEO_STATE_1STEP_OFF), 0);
15091
15092   BackToFront();
15093 }
15094
15095 void GameUndo(int steps)
15096 {
15097   if (!CheckEngineSnapshotList())
15098     return;
15099
15100   LoadEngineSnapshot_Undo(steps);
15101
15102   GameUndoRedoExt();
15103 }
15104
15105 void GameRedo(int steps)
15106 {
15107   if (!CheckEngineSnapshotList())
15108     return;
15109
15110   LoadEngineSnapshot_Redo(steps);
15111
15112   GameUndoRedoExt();
15113 }
15114
15115 static void HandleGameButtonsExt(int id, int button)
15116 {
15117   static boolean game_undo_executed = FALSE;
15118   int steps = BUTTON_STEPSIZE(button);
15119   boolean handle_game_buttons =
15120     (game_status == GAME_MODE_PLAYING ||
15121      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15122
15123   if (!handle_game_buttons)
15124     return;
15125
15126   switch (id)
15127   {
15128     case GAME_CTRL_ID_STOP:
15129       if (game_status == GAME_MODE_MAIN)
15130         break;
15131
15132       if (tape.playing)
15133         TapeStop();
15134       else
15135         RequestQuitGame(TRUE);
15136
15137       break;
15138
15139     case GAME_CTRL_ID_PAUSE:
15140     case GAME_CTRL_ID_PAUSE2:
15141       if (options.network && game_status == GAME_MODE_PLAYING)
15142       {
15143 #if defined(NETWORK_AVALIABLE)
15144         if (tape.pausing)
15145           SendToServer_ContinuePlaying();
15146         else
15147           SendToServer_PausePlaying();
15148 #endif
15149       }
15150       else
15151         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15152
15153       game_undo_executed = FALSE;
15154
15155       break;
15156
15157     case GAME_CTRL_ID_PLAY:
15158       if (game_status == GAME_MODE_MAIN)
15159       {
15160         StartGameActions(options.network, setup.autorecord, level.random_seed);
15161       }
15162       else if (tape.pausing)
15163       {
15164 #if defined(NETWORK_AVALIABLE)
15165         if (options.network)
15166           SendToServer_ContinuePlaying();
15167         else
15168 #endif
15169           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15170       }
15171       break;
15172
15173     case GAME_CTRL_ID_UNDO:
15174       // Important: When using "save snapshot when collecting an item" mode,
15175       // load last (current) snapshot for first "undo" after pressing "pause"
15176       // (else the last-but-one snapshot would be loaded, because the snapshot
15177       // pointer already points to the last snapshot when pressing "pause",
15178       // which is fine for "every step/move" mode, but not for "every collect")
15179       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15180           !game_undo_executed)
15181         steps--;
15182
15183       game_undo_executed = TRUE;
15184
15185       GameUndo(steps);
15186       break;
15187
15188     case GAME_CTRL_ID_REDO:
15189       GameRedo(steps);
15190       break;
15191
15192     case GAME_CTRL_ID_SAVE:
15193       TapeQuickSave();
15194       break;
15195
15196     case GAME_CTRL_ID_LOAD:
15197       TapeQuickLoad();
15198       break;
15199
15200     case SOUND_CTRL_ID_MUSIC:
15201       if (setup.sound_music)
15202       { 
15203         setup.sound_music = FALSE;
15204
15205         FadeMusic();
15206       }
15207       else if (audio.music_available)
15208       { 
15209         setup.sound = setup.sound_music = TRUE;
15210
15211         SetAudioMode(setup.sound);
15212
15213         PlayLevelMusic();
15214       }
15215       break;
15216
15217     case SOUND_CTRL_ID_LOOPS:
15218       if (setup.sound_loops)
15219         setup.sound_loops = FALSE;
15220       else if (audio.loops_available)
15221       {
15222         setup.sound = setup.sound_loops = TRUE;
15223
15224         SetAudioMode(setup.sound);
15225       }
15226       break;
15227
15228     case SOUND_CTRL_ID_SIMPLE:
15229       if (setup.sound_simple)
15230         setup.sound_simple = FALSE;
15231       else if (audio.sound_available)
15232       {
15233         setup.sound = setup.sound_simple = TRUE;
15234
15235         SetAudioMode(setup.sound);
15236       }
15237       break;
15238
15239     default:
15240       break;
15241   }
15242 }
15243
15244 static void HandleGameButtons(struct GadgetInfo *gi)
15245 {
15246   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15247 }
15248
15249 void HandleSoundButtonKeys(Key key)
15250 {
15251
15252   if (key == setup.shortcut.sound_simple)
15253     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15254   else if (key == setup.shortcut.sound_loops)
15255     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15256   else if (key == setup.shortcut.sound_music)
15257     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15258 }