changed redraw system to prevent unneeded screen updates
[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_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   /* ---------- initialize game engine snapshots ---------------------------- */
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     game.snapshot.last_action[i] = 0;
3041   game.snapshot.changed_action = FALSE;
3042   game.snapshot.mode =
3043     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3044      SNAPSHOT_MODE_EVERY_STEP :
3045      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3046      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3047 }
3048
3049 int get_num_special_action(int element, int action_first, int action_last)
3050 {
3051   int num_special_action = 0;
3052   int i, j;
3053
3054   for (i = action_first; i <= action_last; i++)
3055   {
3056     boolean found = FALSE;
3057
3058     for (j = 0; j < NUM_DIRECTIONS; j++)
3059       if (el_act_dir2img(element, i, j) !=
3060           el_act_dir2img(element, ACTION_DEFAULT, j))
3061         found = TRUE;
3062
3063     if (found)
3064       num_special_action++;
3065     else
3066       break;
3067   }
3068
3069   return num_special_action;
3070 }
3071
3072
3073 /*
3074   =============================================================================
3075   InitGame()
3076   -----------------------------------------------------------------------------
3077   initialize and start new game
3078   =============================================================================
3079 */
3080
3081 void InitGame()
3082 {
3083   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3084   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3085
3086   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3087   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3088   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3089   int initial_move_dir = MV_DOWN;
3090   int i, j, x, y;
3091
3092   game_status = GAME_MODE_PLAYING;
3093
3094   StopAnimation();
3095
3096   if (!game.restart_level)
3097     CloseDoor(DOOR_CLOSE_1);
3098
3099   if (level_editor_test_game)
3100     FadeSkipNextFadeIn();
3101   else
3102     FadeSetEnterScreen();
3103
3104   FadeOut(REDRAW_FIELD);
3105
3106   /* needed if different viewport properties defined for playing */
3107   ChangeViewportPropertiesIfNeeded();
3108
3109   DrawCompleteVideoDisplay();
3110
3111   InitGameEngine();
3112   InitGameControlValues();
3113
3114   /* don't play tapes over network */
3115   network_playing = (options.network && !tape.playing);
3116
3117   for (i = 0; i < MAX_PLAYERS; i++)
3118   {
3119     struct PlayerInfo *player = &stored_player[i];
3120
3121     player->index_nr = i;
3122     player->index_bit = (1 << i);
3123     player->element_nr = EL_PLAYER_1 + i;
3124
3125     player->present = FALSE;
3126     player->active = FALSE;
3127     player->mapped = FALSE;
3128
3129     player->killed = FALSE;
3130     player->reanimated = FALSE;
3131
3132     player->action = 0;
3133     player->effective_action = 0;
3134     player->programmed_action = 0;
3135
3136     player->score = 0;
3137     player->score_final = 0;
3138
3139     player->gems_still_needed = level.gems_needed;
3140     player->sokobanfields_still_needed = 0;
3141     player->lights_still_needed = 0;
3142     player->friends_still_needed = 0;
3143
3144     for (j = 0; j < MAX_NUM_KEYS; j++)
3145       player->key[j] = FALSE;
3146
3147     player->num_white_keys = 0;
3148
3149     player->dynabomb_count = 0;
3150     player->dynabomb_size = 1;
3151     player->dynabombs_left = 0;
3152     player->dynabomb_xl = FALSE;
3153
3154     player->MovDir = initial_move_dir;
3155     player->MovPos = 0;
3156     player->GfxPos = 0;
3157     player->GfxDir = initial_move_dir;
3158     player->GfxAction = ACTION_DEFAULT;
3159     player->Frame = 0;
3160     player->StepFrame = 0;
3161
3162     player->initial_element = player->element_nr;
3163     player->artwork_element =
3164       (level.use_artwork_element[i] ? level.artwork_element[i] :
3165        player->element_nr);
3166     player->use_murphy = FALSE;
3167
3168     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3169     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3170
3171     player->gravity = level.initial_player_gravity[i];
3172
3173     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3174
3175     player->actual_frame_counter = 0;
3176
3177     player->step_counter = 0;
3178
3179     player->last_move_dir = initial_move_dir;
3180
3181     player->is_active = FALSE;
3182
3183     player->is_waiting = FALSE;
3184     player->is_moving = FALSE;
3185     player->is_auto_moving = FALSE;
3186     player->is_digging = FALSE;
3187     player->is_snapping = FALSE;
3188     player->is_collecting = FALSE;
3189     player->is_pushing = FALSE;
3190     player->is_switching = FALSE;
3191     player->is_dropping = FALSE;
3192     player->is_dropping_pressed = FALSE;
3193
3194     player->is_bored = FALSE;
3195     player->is_sleeping = FALSE;
3196
3197     player->frame_counter_bored = -1;
3198     player->frame_counter_sleeping = -1;
3199
3200     player->anim_delay_counter = 0;
3201     player->post_delay_counter = 0;
3202
3203     player->dir_waiting = initial_move_dir;
3204     player->action_waiting = ACTION_DEFAULT;
3205     player->last_action_waiting = ACTION_DEFAULT;
3206     player->special_action_bored = ACTION_DEFAULT;
3207     player->special_action_sleeping = ACTION_DEFAULT;
3208
3209     player->switch_x = -1;
3210     player->switch_y = -1;
3211
3212     player->drop_x = -1;
3213     player->drop_y = -1;
3214
3215     player->show_envelope = 0;
3216
3217     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3218
3219     player->push_delay       = -1;      /* initialized when pushing starts */
3220     player->push_delay_value = game.initial_push_delay_value;
3221
3222     player->drop_delay = 0;
3223     player->drop_pressed_delay = 0;
3224
3225     player->last_jx = -1;
3226     player->last_jy = -1;
3227     player->jx = -1;
3228     player->jy = -1;
3229
3230     player->shield_normal_time_left = 0;
3231     player->shield_deadly_time_left = 0;
3232
3233     player->inventory_infinite_element = EL_UNDEFINED;
3234     player->inventory_size = 0;
3235
3236     if (level.use_initial_inventory[i])
3237     {
3238       for (j = 0; j < level.initial_inventory_size[i]; j++)
3239       {
3240         int element = level.initial_inventory_content[i][j];
3241         int collect_count = element_info[element].collect_count_initial;
3242         int k;
3243
3244         if (!IS_CUSTOM_ELEMENT(element))
3245           collect_count = 1;
3246
3247         if (collect_count == 0)
3248           player->inventory_infinite_element = element;
3249         else
3250           for (k = 0; k < collect_count; k++)
3251             if (player->inventory_size < MAX_INVENTORY_SIZE)
3252               player->inventory_element[player->inventory_size++] = element;
3253       }
3254     }
3255
3256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3257     SnapField(player, 0, 0);
3258
3259     player->LevelSolved = FALSE;
3260     player->GameOver = FALSE;
3261
3262     player->LevelSolved_GameWon = FALSE;
3263     player->LevelSolved_GameEnd = FALSE;
3264     player->LevelSolved_PanelOff = FALSE;
3265     player->LevelSolved_SaveTape = FALSE;
3266     player->LevelSolved_SaveScore = FALSE;
3267     player->LevelSolved_CountingTime = 0;
3268     player->LevelSolved_CountingScore = 0;
3269
3270     map_player_action[i] = i;
3271   }
3272
3273   network_player_action_received = FALSE;
3274
3275 #if defined(NETWORK_AVALIABLE)
3276   /* initial null action */
3277   if (network_playing)
3278     SendToServer_MovePlayer(MV_NONE);
3279 #endif
3280
3281   ZX = ZY = -1;
3282   ExitX = ExitY = -1;
3283
3284   FrameCounter = 0;
3285   TimeFrames = 0;
3286   TimePlayed = 0;
3287   TimeLeft = level.time;
3288   TapeTime = 0;
3289
3290   ScreenMovDir = MV_NONE;
3291   ScreenMovPos = 0;
3292   ScreenGfxPos = 0;
3293
3294   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3295
3296   AllPlayersGone = FALSE;
3297
3298   game.no_time_limit = (level.time == 0);
3299
3300   game.yamyam_content_nr = 0;
3301   game.robot_wheel_active = FALSE;
3302   game.magic_wall_active = FALSE;
3303   game.magic_wall_time_left = 0;
3304   game.light_time_left = 0;
3305   game.timegate_time_left = 0;
3306   game.switchgate_pos = 0;
3307   game.wind_direction = level.wind_direction_initial;
3308
3309   game.lenses_time_left = 0;
3310   game.magnify_time_left = 0;
3311
3312   game.ball_state = level.ball_state_initial;
3313   game.ball_content_nr = 0;
3314
3315   game.envelope_active = FALSE;
3316
3317   /* set focus to local player for network games, else to all players */
3318   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3319   game.centered_player_nr_next = game.centered_player_nr;
3320   game.set_centered_player = FALSE;
3321
3322   if (network_playing && tape.recording)
3323   {
3324     /* store client dependent player focus when recording network games */
3325     tape.centered_player_nr_next = game.centered_player_nr_next;
3326     tape.set_centered_player = TRUE;
3327   }
3328
3329   for (i = 0; i < NUM_BELTS; i++)
3330   {
3331     game.belt_dir[i] = MV_NONE;
3332     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3333   }
3334
3335   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3336     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3337
3338 #if DEBUG_INIT_PLAYER
3339   if (options.debug)
3340   {
3341     printf("Player status at level initialization:\n");
3342   }
3343 #endif
3344
3345   SCAN_PLAYFIELD(x, y)
3346   {
3347     Feld[x][y] = level.field[x][y];
3348     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3349     ChangeDelay[x][y] = 0;
3350     ChangePage[x][y] = -1;
3351     CustomValue[x][y] = 0;              /* initialized in InitField() */
3352     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3353     AmoebaNr[x][y] = 0;
3354     WasJustMoving[x][y] = 0;
3355     WasJustFalling[x][y] = 0;
3356     CheckCollision[x][y] = 0;
3357     CheckImpact[x][y] = 0;
3358     Stop[x][y] = FALSE;
3359     Pushed[x][y] = FALSE;
3360
3361     ChangeCount[x][y] = 0;
3362     ChangeEvent[x][y] = -1;
3363
3364     ExplodePhase[x][y] = 0;
3365     ExplodeDelay[x][y] = 0;
3366     ExplodeField[x][y] = EX_TYPE_NONE;
3367
3368     RunnerVisit[x][y] = 0;
3369     PlayerVisit[x][y] = 0;
3370
3371     GfxFrame[x][y] = 0;
3372     GfxRandom[x][y] = INIT_GFX_RANDOM();
3373     GfxElement[x][y] = EL_UNDEFINED;
3374     GfxAction[x][y] = ACTION_DEFAULT;
3375     GfxDir[x][y] = MV_NONE;
3376     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3377   }
3378
3379   SCAN_PLAYFIELD(x, y)
3380   {
3381     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3382       emulate_bd = FALSE;
3383     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3384       emulate_sb = FALSE;
3385     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3386       emulate_sp = FALSE;
3387
3388     InitField(x, y, TRUE);
3389
3390     ResetGfxAnimation(x, y);
3391   }
3392
3393   InitBeltMovement();
3394
3395   for (i = 0; i < MAX_PLAYERS; i++)
3396   {
3397     struct PlayerInfo *player = &stored_player[i];
3398
3399     /* set number of special actions for bored and sleeping animation */
3400     player->num_special_action_bored =
3401       get_num_special_action(player->artwork_element,
3402                              ACTION_BORING_1, ACTION_BORING_LAST);
3403     player->num_special_action_sleeping =
3404       get_num_special_action(player->artwork_element,
3405                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3406   }
3407
3408   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3409                     emulate_sb ? EMU_SOKOBAN :
3410                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3411
3412   /* initialize type of slippery elements */
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414   {
3415     if (!IS_CUSTOM_ELEMENT(i))
3416     {
3417       /* default: elements slip down either to the left or right randomly */
3418       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3419
3420       /* SP style elements prefer to slip down on the left side */
3421       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3422         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3423
3424       /* BD style elements prefer to slip down on the left side */
3425       if (game.emulation == EMU_BOULDERDASH)
3426         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3427     }
3428   }
3429
3430   /* initialize explosion and ignition delay */
3431   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432   {
3433     if (!IS_CUSTOM_ELEMENT(i))
3434     {
3435       int num_phase = 8;
3436       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3437                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3438                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3439       int last_phase = (num_phase + 1) * delay;
3440       int half_phase = (num_phase / 2) * delay;
3441
3442       element_info[i].explosion_delay = last_phase - 1;
3443       element_info[i].ignition_delay = half_phase;
3444
3445       if (i == EL_BLACK_ORB)
3446         element_info[i].ignition_delay = 1;
3447     }
3448   }
3449
3450   /* correct non-moving belts to start moving left */
3451   for (i = 0; i < NUM_BELTS; i++)
3452     if (game.belt_dir[i] == MV_NONE)
3453       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3454
3455 #if USE_NEW_PLAYER_ASSIGNMENTS
3456   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3457   /* choose default local player */
3458   local_player = &stored_player[0];
3459
3460   for (i = 0; i < MAX_PLAYERS; i++)
3461     stored_player[i].connected = FALSE;
3462
3463   local_player->connected = TRUE;
3464   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3465
3466   if (tape.playing)
3467   {
3468     for (i = 0; i < MAX_PLAYERS; i++)
3469       stored_player[i].connected = tape.player_participates[i];
3470   }
3471   else if (game.team_mode && !options.network)
3472   {
3473     /* try to guess locally connected team mode players (needed for correct
3474        assignment of player figures from level to locally playing players) */
3475
3476     for (i = 0; i < MAX_PLAYERS; i++)
3477       if (setup.input[i].use_joystick ||
3478           setup.input[i].key.left != KSYM_UNDEFINED)
3479         stored_player[i].connected = TRUE;
3480   }
3481
3482 #if DEBUG_INIT_PLAYER
3483   if (options.debug)
3484   {
3485     printf("Player status after level initialization:\n");
3486
3487     for (i = 0; i < MAX_PLAYERS; i++)
3488     {
3489       struct PlayerInfo *player = &stored_player[i];
3490
3491       printf("- player %d: present == %d, connected == %d, active == %d",
3492              i + 1,
3493              player->present,
3494              player->connected,
3495              player->active);
3496
3497       if (local_player == player)
3498         printf(" (local player)");
3499
3500       printf("\n");
3501     }
3502   }
3503 #endif
3504
3505 #if DEBUG_INIT_PLAYER
3506   if (options.debug)
3507     printf("Reassigning players ...\n");
3508 #endif
3509
3510   /* check if any connected player was not found in playfield */
3511   for (i = 0; i < MAX_PLAYERS; i++)
3512   {
3513     struct PlayerInfo *player = &stored_player[i];
3514
3515     if (player->connected && !player->present)
3516     {
3517       struct PlayerInfo *field_player = NULL;
3518
3519 #if DEBUG_INIT_PLAYER
3520       if (options.debug)
3521         printf("- looking for field player for player %d ...\n", i + 1);
3522 #endif
3523
3524       /* assign first free player found that is present in the playfield */
3525
3526       /* first try: look for unmapped playfield player that is not connected */
3527       for (j = 0; j < MAX_PLAYERS; j++)
3528         if (field_player == NULL &&
3529             stored_player[j].present &&
3530             !stored_player[j].mapped &&
3531             !stored_player[j].connected)
3532           field_player = &stored_player[j];
3533
3534       /* second try: look for *any* unmapped playfield player */
3535       for (j = 0; j < MAX_PLAYERS; j++)
3536         if (field_player == NULL &&
3537             stored_player[j].present &&
3538             !stored_player[j].mapped)
3539           field_player = &stored_player[j];
3540
3541       if (field_player != NULL)
3542       {
3543         int jx = field_player->jx, jy = field_player->jy;
3544
3545 #if DEBUG_INIT_PLAYER
3546         if (options.debug)
3547           printf("- found player %d\n", field_player->index_nr + 1);
3548 #endif
3549
3550         player->present = FALSE;
3551         player->active = FALSE;
3552
3553         field_player->present = TRUE;
3554         field_player->active = TRUE;
3555
3556         /*
3557         player->initial_element = field_player->initial_element;
3558         player->artwork_element = field_player->artwork_element;
3559
3560         player->block_last_field       = field_player->block_last_field;
3561         player->block_delay_adjustment = field_player->block_delay_adjustment;
3562         */
3563
3564         StorePlayer[jx][jy] = field_player->element_nr;
3565
3566         field_player->jx = field_player->last_jx = jx;
3567         field_player->jy = field_player->last_jy = jy;
3568
3569         if (local_player == player)
3570           local_player = field_player;
3571
3572         map_player_action[field_player->index_nr] = i;
3573
3574         field_player->mapped = TRUE;
3575
3576 #if DEBUG_INIT_PLAYER
3577         if (options.debug)
3578           printf("- map_player_action[%d] == %d\n",
3579                  field_player->index_nr + 1, i + 1);
3580 #endif
3581       }
3582     }
3583
3584     if (player->connected && player->present)
3585       player->mapped = TRUE;
3586   }
3587
3588 #if DEBUG_INIT_PLAYER
3589   if (options.debug)
3590   {
3591     printf("Player status after player assignment (first stage):\n");
3592
3593     for (i = 0; i < MAX_PLAYERS; i++)
3594     {
3595       struct PlayerInfo *player = &stored_player[i];
3596
3597       printf("- player %d: present == %d, connected == %d, active == %d",
3598              i + 1,
3599              player->present,
3600              player->connected,
3601              player->active);
3602
3603       if (local_player == player)
3604         printf(" (local player)");
3605
3606       printf("\n");
3607     }
3608   }
3609 #endif
3610
3611 #else
3612
3613   /* check if any connected player was not found in playfield */
3614   for (i = 0; i < MAX_PLAYERS; i++)
3615   {
3616     struct PlayerInfo *player = &stored_player[i];
3617
3618     if (player->connected && !player->present)
3619     {
3620       for (j = 0; j < MAX_PLAYERS; j++)
3621       {
3622         struct PlayerInfo *field_player = &stored_player[j];
3623         int jx = field_player->jx, jy = field_player->jy;
3624
3625         /* assign first free player found that is present in the playfield */
3626         if (field_player->present && !field_player->connected)
3627         {
3628           player->present = TRUE;
3629           player->active = TRUE;
3630
3631           field_player->present = FALSE;
3632           field_player->active = FALSE;
3633
3634           player->initial_element = field_player->initial_element;
3635           player->artwork_element = field_player->artwork_element;
3636
3637           player->block_last_field       = field_player->block_last_field;
3638           player->block_delay_adjustment = field_player->block_delay_adjustment;
3639
3640           StorePlayer[jx][jy] = player->element_nr;
3641
3642           player->jx = player->last_jx = jx;
3643           player->jy = player->last_jy = jy;
3644
3645           break;
3646         }
3647       }
3648     }
3649   }
3650 #endif
3651
3652 #if 0
3653   printf("::: local_player->present == %d\n", local_player->present);
3654 #endif
3655
3656   if (tape.playing)
3657   {
3658     /* when playing a tape, eliminate all players who do not participate */
3659
3660 #if USE_NEW_PLAYER_ASSIGNMENTS
3661
3662     if (!game.team_mode)
3663     {
3664       for (i = 0; i < MAX_PLAYERS; i++)
3665       {
3666         if (stored_player[i].active &&
3667             !tape.player_participates[map_player_action[i]])
3668         {
3669           struct PlayerInfo *player = &stored_player[i];
3670           int jx = player->jx, jy = player->jy;
3671
3672 #if DEBUG_INIT_PLAYER
3673           if (options.debug)
3674             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3675 #endif
3676
3677           player->active = FALSE;
3678           StorePlayer[jx][jy] = 0;
3679           Feld[jx][jy] = EL_EMPTY;
3680         }
3681       }
3682     }
3683
3684 #else
3685
3686     for (i = 0; i < MAX_PLAYERS; i++)
3687     {
3688       if (stored_player[i].active &&
3689           !tape.player_participates[i])
3690       {
3691         struct PlayerInfo *player = &stored_player[i];
3692         int jx = player->jx, jy = player->jy;
3693
3694         player->active = FALSE;
3695         StorePlayer[jx][jy] = 0;
3696         Feld[jx][jy] = EL_EMPTY;
3697       }
3698     }
3699 #endif
3700   }
3701   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3702   {
3703     /* when in single player mode, eliminate all but the first active player */
3704
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706     {
3707       if (stored_player[i].active)
3708       {
3709         for (j = i + 1; j < MAX_PLAYERS; j++)
3710         {
3711           if (stored_player[j].active)
3712           {
3713             struct PlayerInfo *player = &stored_player[j];
3714             int jx = player->jx, jy = player->jy;
3715
3716             player->active = FALSE;
3717             player->present = FALSE;
3718
3719             StorePlayer[jx][jy] = 0;
3720             Feld[jx][jy] = EL_EMPTY;
3721           }
3722         }
3723       }
3724     }
3725   }
3726
3727   /* when recording the game, store which players take part in the game */
3728   if (tape.recording)
3729   {
3730 #if USE_NEW_PLAYER_ASSIGNMENTS
3731     for (i = 0; i < MAX_PLAYERS; i++)
3732       if (stored_player[i].connected)
3733         tape.player_participates[i] = TRUE;
3734 #else
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736       if (stored_player[i].active)
3737         tape.player_participates[i] = TRUE;
3738 #endif
3739   }
3740
3741 #if DEBUG_INIT_PLAYER
3742   if (options.debug)
3743   {
3744     printf("Player status after player assignment (final stage):\n");
3745
3746     for (i = 0; i < MAX_PLAYERS; i++)
3747     {
3748       struct PlayerInfo *player = &stored_player[i];
3749
3750       printf("- player %d: present == %d, connected == %d, active == %d",
3751              i + 1,
3752              player->present,
3753              player->connected,
3754              player->active);
3755
3756       if (local_player == player)
3757         printf(" (local player)");
3758
3759       printf("\n");
3760     }
3761   }
3762 #endif
3763
3764   if (BorderElement == EL_EMPTY)
3765   {
3766     SBX_Left = 0;
3767     SBX_Right = lev_fieldx - SCR_FIELDX;
3768     SBY_Upper = 0;
3769     SBY_Lower = lev_fieldy - SCR_FIELDY;
3770   }
3771   else
3772   {
3773     SBX_Left = -1;
3774     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3775     SBY_Upper = -1;
3776     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3777   }
3778
3779   if (full_lev_fieldx <= SCR_FIELDX)
3780     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3781   if (full_lev_fieldy <= SCR_FIELDY)
3782     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3783
3784   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3785     SBX_Left--;
3786   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3787     SBY_Upper--;
3788
3789   /* if local player not found, look for custom element that might create
3790      the player (make some assumptions about the right custom element) */
3791   if (!local_player->present)
3792   {
3793     int start_x = 0, start_y = 0;
3794     int found_rating = 0;
3795     int found_element = EL_UNDEFINED;
3796     int player_nr = local_player->index_nr;
3797
3798     SCAN_PLAYFIELD(x, y)
3799     {
3800       int element = Feld[x][y];
3801       int content;
3802       int xx, yy;
3803       boolean is_player;
3804
3805       if (level.use_start_element[player_nr] &&
3806           level.start_element[player_nr] == element &&
3807           found_rating < 4)
3808       {
3809         start_x = x;
3810         start_y = y;
3811
3812         found_rating = 4;
3813         found_element = element;
3814       }
3815
3816       if (!IS_CUSTOM_ELEMENT(element))
3817         continue;
3818
3819       if (CAN_CHANGE(element))
3820       {
3821         for (i = 0; i < element_info[element].num_change_pages; i++)
3822         {
3823           /* check for player created from custom element as single target */
3824           content = element_info[element].change_page[i].target_element;
3825           is_player = ELEM_IS_PLAYER(content);
3826
3827           if (is_player && (found_rating < 3 ||
3828                             (found_rating == 3 && element < found_element)))
3829           {
3830             start_x = x;
3831             start_y = y;
3832
3833             found_rating = 3;
3834             found_element = element;
3835           }
3836         }
3837       }
3838
3839       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3840       {
3841         /* check for player created from custom element as explosion content */
3842         content = element_info[element].content.e[xx][yy];
3843         is_player = ELEM_IS_PLAYER(content);
3844
3845         if (is_player && (found_rating < 2 ||
3846                           (found_rating == 2 && element < found_element)))
3847         {
3848           start_x = x + xx - 1;
3849           start_y = y + yy - 1;
3850
3851           found_rating = 2;
3852           found_element = element;
3853         }
3854
3855         if (!CAN_CHANGE(element))
3856           continue;
3857
3858         for (i = 0; i < element_info[element].num_change_pages; i++)
3859         {
3860           /* check for player created from custom element as extended target */
3861           content =
3862             element_info[element].change_page[i].target_content.e[xx][yy];
3863
3864           is_player = ELEM_IS_PLAYER(content);
3865
3866           if (is_player && (found_rating < 1 ||
3867                             (found_rating == 1 && element < found_element)))
3868           {
3869             start_x = x + xx - 1;
3870             start_y = y + yy - 1;
3871
3872             found_rating = 1;
3873             found_element = element;
3874           }
3875         }
3876       }
3877     }
3878
3879     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3880                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3881                 start_x - MIDPOSX);
3882
3883     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3884                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3885                 start_y - MIDPOSY);
3886   }
3887   else
3888   {
3889     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3891                 local_player->jx - MIDPOSX);
3892
3893     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 local_player->jy - MIDPOSY);
3896   }
3897
3898   /* !!! FIX THIS (START) !!! */
3899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3900   {
3901     InitGameEngine_EM();
3902   }
3903   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3904   {
3905     InitGameEngine_SP();
3906   }
3907   else
3908   {
3909     DrawLevel(REDRAW_FIELD);
3910     DrawAllPlayers();
3911
3912     /* after drawing the level, correct some elements */
3913     if (game.timegate_time_left == 0)
3914       CloseAllOpenTimegates();
3915   }
3916
3917   /* blit playfield from scroll buffer to normal back buffer for fading in */
3918   BlitScreenToBitmap(backbuffer);
3919
3920   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3921   /* !!! FIX THIS (END) !!! */
3922
3923   FadeIn(REDRAW_FIELD);
3924
3925 #if 1
3926   // full screen redraw is required at this point in the following cases:
3927   // - special editor door undrawn when game was started from level editor
3928   // - drawing area (playfield) was changed and has to be removed completely
3929   redraw_mask = REDRAW_ALL;
3930   BackToFront();
3931 #endif
3932
3933   if (!game.restart_level)
3934   {
3935     /* copy default game door content to main double buffer */
3936
3937     /* !!! CHECK AGAIN !!! */
3938     SetPanelBackground();
3939     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3940     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3941   }
3942
3943   SetPanelBackground();
3944   SetDrawBackgroundMask(REDRAW_DOOR_1);
3945
3946   UpdateAndDisplayGameControlValues();
3947
3948   if (!game.restart_level)
3949   {
3950     UnmapGameButtons();
3951     UnmapTapeButtons();
3952     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3953     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3954     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3955     MapGameButtons();
3956     MapTapeButtons();
3957
3958     /* copy actual game door content to door double buffer for OpenDoor() */
3959     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3960
3961     OpenDoor(DOOR_OPEN_ALL);
3962
3963     PlaySound(SND_GAME_STARTING);
3964
3965     if (setup.sound_music)
3966       PlayLevelMusic();
3967
3968     KeyboardAutoRepeatOffUnlessAutoplay();
3969
3970 #if DEBUG_INIT_PLAYER
3971     if (options.debug)
3972     {
3973       printf("Player status (final):\n");
3974
3975       for (i = 0; i < MAX_PLAYERS; i++)
3976       {
3977         struct PlayerInfo *player = &stored_player[i];
3978
3979         printf("- player %d: present == %d, connected == %d, active == %d",
3980                i + 1,
3981                player->present,
3982                player->connected,
3983                player->active);
3984
3985         if (local_player == player)
3986           printf(" (local player)");
3987
3988         printf("\n");
3989       }
3990     }
3991 #endif
3992   }
3993
3994   UnmapAllGadgets();
3995
3996   MapGameButtons();
3997   MapTapeButtons();
3998
3999   if (!game.restart_level && !tape.playing)
4000   {
4001     LevelStats_incPlayed(level_nr);
4002
4003     SaveLevelSetup_SeriesInfo();
4004   }
4005
4006   game.restart_level = FALSE;
4007
4008   SaveEngineSnapshotToListInitial();
4009 }
4010
4011 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4012 {
4013   /* this is used for non-R'n'D game engines to update certain engine values */
4014
4015   /* needed to determine if sounds are played within the visible screen area */
4016   scroll_x = actual_scroll_x;
4017   scroll_y = actual_scroll_y;
4018 }
4019
4020 void InitMovDir(int x, int y)
4021 {
4022   int i, element = Feld[x][y];
4023   static int xy[4][2] =
4024   {
4025     {  0, +1 },
4026     { +1,  0 },
4027     {  0, -1 },
4028     { -1,  0 }
4029   };
4030   static int direction[3][4] =
4031   {
4032     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4033     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4034     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4035   };
4036
4037   switch (element)
4038   {
4039     case EL_BUG_RIGHT:
4040     case EL_BUG_UP:
4041     case EL_BUG_LEFT:
4042     case EL_BUG_DOWN:
4043       Feld[x][y] = EL_BUG;
4044       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4045       break;
4046
4047     case EL_SPACESHIP_RIGHT:
4048     case EL_SPACESHIP_UP:
4049     case EL_SPACESHIP_LEFT:
4050     case EL_SPACESHIP_DOWN:
4051       Feld[x][y] = EL_SPACESHIP;
4052       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4053       break;
4054
4055     case EL_BD_BUTTERFLY_RIGHT:
4056     case EL_BD_BUTTERFLY_UP:
4057     case EL_BD_BUTTERFLY_LEFT:
4058     case EL_BD_BUTTERFLY_DOWN:
4059       Feld[x][y] = EL_BD_BUTTERFLY;
4060       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4061       break;
4062
4063     case EL_BD_FIREFLY_RIGHT:
4064     case EL_BD_FIREFLY_UP:
4065     case EL_BD_FIREFLY_LEFT:
4066     case EL_BD_FIREFLY_DOWN:
4067       Feld[x][y] = EL_BD_FIREFLY;
4068       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4069       break;
4070
4071     case EL_PACMAN_RIGHT:
4072     case EL_PACMAN_UP:
4073     case EL_PACMAN_LEFT:
4074     case EL_PACMAN_DOWN:
4075       Feld[x][y] = EL_PACMAN;
4076       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4077       break;
4078
4079     case EL_YAMYAM_LEFT:
4080     case EL_YAMYAM_RIGHT:
4081     case EL_YAMYAM_UP:
4082     case EL_YAMYAM_DOWN:
4083       Feld[x][y] = EL_YAMYAM;
4084       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4085       break;
4086
4087     case EL_SP_SNIKSNAK:
4088       MovDir[x][y] = MV_UP;
4089       break;
4090
4091     case EL_SP_ELECTRON:
4092       MovDir[x][y] = MV_LEFT;
4093       break;
4094
4095     case EL_MOLE_LEFT:
4096     case EL_MOLE_RIGHT:
4097     case EL_MOLE_UP:
4098     case EL_MOLE_DOWN:
4099       Feld[x][y] = EL_MOLE;
4100       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4101       break;
4102
4103     default:
4104       if (IS_CUSTOM_ELEMENT(element))
4105       {
4106         struct ElementInfo *ei = &element_info[element];
4107         int move_direction_initial = ei->move_direction_initial;
4108         int move_pattern = ei->move_pattern;
4109
4110         if (move_direction_initial == MV_START_PREVIOUS)
4111         {
4112           if (MovDir[x][y] != MV_NONE)
4113             return;
4114
4115           move_direction_initial = MV_START_AUTOMATIC;
4116         }
4117
4118         if (move_direction_initial == MV_START_RANDOM)
4119           MovDir[x][y] = 1 << RND(4);
4120         else if (move_direction_initial & MV_ANY_DIRECTION)
4121           MovDir[x][y] = move_direction_initial;
4122         else if (move_pattern == MV_ALL_DIRECTIONS ||
4123                  move_pattern == MV_TURNING_LEFT ||
4124                  move_pattern == MV_TURNING_RIGHT ||
4125                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4126                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4127                  move_pattern == MV_TURNING_RANDOM)
4128           MovDir[x][y] = 1 << RND(4);
4129         else if (move_pattern == MV_HORIZONTAL)
4130           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4131         else if (move_pattern == MV_VERTICAL)
4132           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4133         else if (move_pattern & MV_ANY_DIRECTION)
4134           MovDir[x][y] = element_info[element].move_pattern;
4135         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4136                  move_pattern == MV_ALONG_RIGHT_SIDE)
4137         {
4138           /* use random direction as default start direction */
4139           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4140             MovDir[x][y] = 1 << RND(4);
4141
4142           for (i = 0; i < NUM_DIRECTIONS; i++)
4143           {
4144             int x1 = x + xy[i][0];
4145             int y1 = y + xy[i][1];
4146
4147             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4148             {
4149               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4150                 MovDir[x][y] = direction[0][i];
4151               else
4152                 MovDir[x][y] = direction[1][i];
4153
4154               break;
4155             }
4156           }
4157         }                
4158       }
4159       else
4160       {
4161         MovDir[x][y] = 1 << RND(4);
4162
4163         if (element != EL_BUG &&
4164             element != EL_SPACESHIP &&
4165             element != EL_BD_BUTTERFLY &&
4166             element != EL_BD_FIREFLY)
4167           break;
4168
4169         for (i = 0; i < NUM_DIRECTIONS; i++)
4170         {
4171           int x1 = x + xy[i][0];
4172           int y1 = y + xy[i][1];
4173
4174           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4175           {
4176             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4177             {
4178               MovDir[x][y] = direction[0][i];
4179               break;
4180             }
4181             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4182                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4183             {
4184               MovDir[x][y] = direction[1][i];
4185               break;
4186             }
4187           }
4188         }
4189       }
4190       break;
4191   }
4192
4193   GfxDir[x][y] = MovDir[x][y];
4194 }
4195
4196 void InitAmoebaNr(int x, int y)
4197 {
4198   int i;
4199   int group_nr = AmoebeNachbarNr(x, y);
4200
4201   if (group_nr == 0)
4202   {
4203     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4204     {
4205       if (AmoebaCnt[i] == 0)
4206       {
4207         group_nr = i;
4208         break;
4209       }
4210     }
4211   }
4212
4213   AmoebaNr[x][y] = group_nr;
4214   AmoebaCnt[group_nr]++;
4215   AmoebaCnt2[group_nr]++;
4216 }
4217
4218 static void PlayerWins(struct PlayerInfo *player)
4219 {
4220   player->LevelSolved = TRUE;
4221   player->GameOver = TRUE;
4222
4223   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4224                          level.native_em_level->lev->score : player->score);
4225
4226   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4227                                       TimeLeft);
4228   player->LevelSolved_CountingScore = player->score_final;
4229 }
4230
4231 void GameWon()
4232 {
4233   static int time, time_final;
4234   static int score, score_final;
4235   static int game_over_delay_1 = 0;
4236   static int game_over_delay_2 = 0;
4237   int game_over_delay_value_1 = 50;
4238   int game_over_delay_value_2 = 50;
4239
4240   if (!local_player->LevelSolved_GameWon)
4241   {
4242     int i;
4243
4244     /* do not start end game actions before the player stops moving (to exit) */
4245     if (local_player->MovPos)
4246       return;
4247
4248     local_player->LevelSolved_GameWon = TRUE;
4249     local_player->LevelSolved_SaveTape = tape.recording;
4250     local_player->LevelSolved_SaveScore = !tape.playing;
4251
4252     if (!tape.playing)
4253     {
4254       LevelStats_incSolved(level_nr);
4255
4256       SaveLevelSetup_SeriesInfo();
4257     }
4258
4259     if (tape.auto_play)         /* tape might already be stopped here */
4260       tape.auto_play_level_solved = TRUE;
4261
4262     TapeStop();
4263
4264     game_over_delay_1 = game_over_delay_value_1;
4265     game_over_delay_2 = game_over_delay_value_2;
4266
4267     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4268     score = score_final = local_player->score_final;
4269
4270     if (TimeLeft > 0)
4271     {
4272       time_final = 0;
4273       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4274     }
4275     else if (game.no_time_limit && TimePlayed < 999)
4276     {
4277       time_final = 999;
4278       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4279     }
4280
4281     local_player->score_final = score_final;
4282
4283     if (level_editor_test_game)
4284     {
4285       time = time_final;
4286       score = score_final;
4287
4288       local_player->LevelSolved_CountingTime = time;
4289       local_player->LevelSolved_CountingScore = score;
4290
4291       game_panel_controls[GAME_PANEL_TIME].value = time;
4292       game_panel_controls[GAME_PANEL_SCORE].value = score;
4293
4294       DisplayGameControlValues();
4295     }
4296
4297     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4298     {
4299       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4300       {
4301         /* close exit door after last player */
4302         if ((AllPlayersGone &&
4303              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4304               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4305               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4306             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4307             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4308         {
4309           int element = Feld[ExitX][ExitY];
4310
4311           Feld[ExitX][ExitY] =
4312             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4313              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4314              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4315              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4316              EL_EM_STEEL_EXIT_CLOSING);
4317
4318           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4319         }
4320
4321         /* player disappears */
4322         DrawLevelField(ExitX, ExitY);
4323       }
4324
4325       for (i = 0; i < MAX_PLAYERS; i++)
4326       {
4327         struct PlayerInfo *player = &stored_player[i];
4328
4329         if (player->present)
4330         {
4331           RemovePlayer(player);
4332
4333           /* player disappears */
4334           DrawLevelField(player->jx, player->jy);
4335         }
4336       }
4337     }
4338
4339     PlaySound(SND_GAME_WINNING);
4340   }
4341
4342   if (game_over_delay_1 > 0)
4343   {
4344     game_over_delay_1--;
4345
4346     return;
4347   }
4348
4349   if (time != time_final)
4350   {
4351     int time_to_go = ABS(time_final - time);
4352     int time_count_dir = (time < time_final ? +1 : -1);
4353     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4354
4355     time  += time_count_steps * time_count_dir;
4356     score += time_count_steps * level.score[SC_TIME_BONUS];
4357
4358     local_player->LevelSolved_CountingTime = time;
4359     local_player->LevelSolved_CountingScore = score;
4360
4361     game_panel_controls[GAME_PANEL_TIME].value = time;
4362     game_panel_controls[GAME_PANEL_SCORE].value = score;
4363
4364     DisplayGameControlValues();
4365
4366     if (time == time_final)
4367       StopSound(SND_GAME_LEVELTIME_BONUS);
4368     else if (setup.sound_loops)
4369       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4370     else
4371       PlaySound(SND_GAME_LEVELTIME_BONUS);
4372
4373     return;
4374   }
4375
4376   local_player->LevelSolved_PanelOff = TRUE;
4377
4378   if (game_over_delay_2 > 0)
4379   {
4380     game_over_delay_2--;
4381
4382     return;
4383   }
4384
4385   GameEnd();
4386 }
4387
4388 void GameEnd()
4389 {
4390   int hi_pos;
4391   boolean raise_level = FALSE;
4392
4393   local_player->LevelSolved_GameEnd = TRUE;
4394
4395   CloseDoor(DOOR_CLOSE_1);
4396
4397   if (local_player->LevelSolved_SaveTape)
4398   {
4399     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4400   }
4401
4402   if (level_editor_test_game)
4403   {
4404     game_status = GAME_MODE_MAIN;
4405
4406     DrawAndFadeInMainMenu(REDRAW_FIELD);
4407
4408     return;
4409   }
4410
4411   if (!local_player->LevelSolved_SaveScore)
4412   {
4413     FadeOut(REDRAW_FIELD);
4414
4415     game_status = GAME_MODE_MAIN;
4416
4417     DrawAndFadeInMainMenu(REDRAW_FIELD);
4418
4419     return;
4420   }
4421
4422   if (level_nr == leveldir_current->handicap_level)
4423   {
4424     leveldir_current->handicap_level++;
4425
4426     SaveLevelSetup_SeriesInfo();
4427   }
4428
4429   if (level_nr < leveldir_current->last_level)
4430     raise_level = TRUE;                 /* advance to next level */
4431
4432   if ((hi_pos = NewHiScore()) >= 0) 
4433   {
4434     game_status = GAME_MODE_SCORES;
4435
4436     /* needed if different viewport properties defined for scores */
4437     ChangeViewportPropertiesIfNeeded();
4438
4439     DrawHallOfFame(hi_pos);
4440
4441     if (raise_level)
4442     {
4443       level_nr++;
4444       TapeErase();
4445     }
4446   }
4447   else
4448   {
4449     FadeOut(REDRAW_FIELD);
4450
4451     game_status = GAME_MODE_MAIN;
4452
4453     if (raise_level)
4454     {
4455       level_nr++;
4456       TapeErase();
4457     }
4458
4459     DrawAndFadeInMainMenu(REDRAW_FIELD);
4460   }
4461 }
4462
4463 int NewHiScore()
4464 {
4465   int k, l;
4466   int position = -1;
4467
4468   LoadScore(level_nr);
4469
4470   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4471       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4472     return -1;
4473
4474   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4475   {
4476     if (local_player->score_final > highscore[k].Score)
4477     {
4478       /* player has made it to the hall of fame */
4479
4480       if (k < MAX_SCORE_ENTRIES - 1)
4481       {
4482         int m = MAX_SCORE_ENTRIES - 1;
4483
4484 #ifdef ONE_PER_NAME
4485         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4486           if (strEqual(setup.player_name, highscore[l].Name))
4487             m = l;
4488         if (m == k)     /* player's new highscore overwrites his old one */
4489           goto put_into_list;
4490 #endif
4491
4492         for (l = m; l > k; l--)
4493         {
4494           strcpy(highscore[l].Name, highscore[l - 1].Name);
4495           highscore[l].Score = highscore[l - 1].Score;
4496         }
4497       }
4498
4499 #ifdef ONE_PER_NAME
4500       put_into_list:
4501 #endif
4502       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4503       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4504       highscore[k].Score = local_player->score_final; 
4505       position = k;
4506       break;
4507     }
4508
4509 #ifdef ONE_PER_NAME
4510     else if (!strncmp(setup.player_name, highscore[k].Name,
4511                       MAX_PLAYER_NAME_LEN))
4512       break;    /* player already there with a higher score */
4513 #endif
4514
4515   }
4516
4517   if (position >= 0) 
4518     SaveScore(level_nr);
4519
4520   return position;
4521 }
4522
4523 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4524 {
4525   int element = Feld[x][y];
4526   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4527   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4528   int horiz_move = (dx != 0);
4529   int sign = (horiz_move ? dx : dy);
4530   int step = sign * element_info[element].move_stepsize;
4531
4532   /* special values for move stepsize for spring and things on conveyor belt */
4533   if (horiz_move)
4534   {
4535     if (CAN_FALL(element) &&
4536         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4537       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4538     else if (element == EL_SPRING)
4539       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4540   }
4541
4542   return step;
4543 }
4544
4545 inline static int getElementMoveStepsize(int x, int y)
4546 {
4547   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4548 }
4549
4550 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4551 {
4552   if (player->GfxAction != action || player->GfxDir != dir)
4553   {
4554     player->GfxAction = action;
4555     player->GfxDir = dir;
4556     player->Frame = 0;
4557     player->StepFrame = 0;
4558   }
4559 }
4560
4561 static void ResetGfxFrame(int x, int y, boolean redraw)
4562 {
4563   int element = Feld[x][y];
4564   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4565   int last_gfx_frame = GfxFrame[x][y];
4566
4567   if (graphic_info[graphic].anim_global_sync)
4568     GfxFrame[x][y] = FrameCounter;
4569   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4570     GfxFrame[x][y] = CustomValue[x][y];
4571   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4572     GfxFrame[x][y] = element_info[element].collect_score;
4573   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4574     GfxFrame[x][y] = ChangeDelay[x][y];
4575
4576   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4577     DrawLevelGraphicAnimation(x, y, graphic);
4578 }
4579
4580 static void ResetGfxAnimation(int x, int y)
4581 {
4582   GfxAction[x][y] = ACTION_DEFAULT;
4583   GfxDir[x][y] = MovDir[x][y];
4584   GfxFrame[x][y] = 0;
4585
4586   ResetGfxFrame(x, y, FALSE);
4587 }
4588
4589 static void ResetRandomAnimationValue(int x, int y)
4590 {
4591   GfxRandom[x][y] = INIT_GFX_RANDOM();
4592 }
4593
4594 void InitMovingField(int x, int y, int direction)
4595 {
4596   int element = Feld[x][y];
4597   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4598   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4599   int newx = x + dx;
4600   int newy = y + dy;
4601   boolean is_moving_before, is_moving_after;
4602
4603   /* check if element was/is moving or being moved before/after mode change */
4604   is_moving_before = (WasJustMoving[x][y] != 0);
4605   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4606
4607   /* reset animation only for moving elements which change direction of moving
4608      or which just started or stopped moving
4609      (else CEs with property "can move" / "not moving" are reset each frame) */
4610   if (is_moving_before != is_moving_after ||
4611       direction != MovDir[x][y])
4612     ResetGfxAnimation(x, y);
4613
4614   MovDir[x][y] = direction;
4615   GfxDir[x][y] = direction;
4616
4617   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4618                      direction == MV_DOWN && CAN_FALL(element) ?
4619                      ACTION_FALLING : ACTION_MOVING);
4620
4621   /* this is needed for CEs with property "can move" / "not moving" */
4622
4623   if (is_moving_after)
4624   {
4625     if (Feld[newx][newy] == EL_EMPTY)
4626       Feld[newx][newy] = EL_BLOCKED;
4627
4628     MovDir[newx][newy] = MovDir[x][y];
4629
4630     CustomValue[newx][newy] = CustomValue[x][y];
4631
4632     GfxFrame[newx][newy] = GfxFrame[x][y];
4633     GfxRandom[newx][newy] = GfxRandom[x][y];
4634     GfxAction[newx][newy] = GfxAction[x][y];
4635     GfxDir[newx][newy] = GfxDir[x][y];
4636   }
4637 }
4638
4639 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4640 {
4641   int direction = MovDir[x][y];
4642   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4643   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4644
4645   *goes_to_x = newx;
4646   *goes_to_y = newy;
4647 }
4648
4649 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4650 {
4651   int oldx = x, oldy = y;
4652   int direction = MovDir[x][y];
4653
4654   if (direction == MV_LEFT)
4655     oldx++;
4656   else if (direction == MV_RIGHT)
4657     oldx--;
4658   else if (direction == MV_UP)
4659     oldy++;
4660   else if (direction == MV_DOWN)
4661     oldy--;
4662
4663   *comes_from_x = oldx;
4664   *comes_from_y = oldy;
4665 }
4666
4667 int MovingOrBlocked2Element(int x, int y)
4668 {
4669   int element = Feld[x][y];
4670
4671   if (element == EL_BLOCKED)
4672   {
4673     int oldx, oldy;
4674
4675     Blocked2Moving(x, y, &oldx, &oldy);
4676     return Feld[oldx][oldy];
4677   }
4678   else
4679     return element;
4680 }
4681
4682 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4683 {
4684   /* like MovingOrBlocked2Element(), but if element is moving
4685      and (x,y) is the field the moving element is just leaving,
4686      return EL_BLOCKED instead of the element value */
4687   int element = Feld[x][y];
4688
4689   if (IS_MOVING(x, y))
4690   {
4691     if (element == EL_BLOCKED)
4692     {
4693       int oldx, oldy;
4694
4695       Blocked2Moving(x, y, &oldx, &oldy);
4696       return Feld[oldx][oldy];
4697     }
4698     else
4699       return EL_BLOCKED;
4700   }
4701   else
4702     return element;
4703 }
4704
4705 static void RemoveField(int x, int y)
4706 {
4707   Feld[x][y] = EL_EMPTY;
4708
4709   MovPos[x][y] = 0;
4710   MovDir[x][y] = 0;
4711   MovDelay[x][y] = 0;
4712
4713   CustomValue[x][y] = 0;
4714
4715   AmoebaNr[x][y] = 0;
4716   ChangeDelay[x][y] = 0;
4717   ChangePage[x][y] = -1;
4718   Pushed[x][y] = FALSE;
4719
4720   GfxElement[x][y] = EL_UNDEFINED;
4721   GfxAction[x][y] = ACTION_DEFAULT;
4722   GfxDir[x][y] = MV_NONE;
4723 }
4724
4725 void RemoveMovingField(int x, int y)
4726 {
4727   int oldx = x, oldy = y, newx = x, newy = y;
4728   int element = Feld[x][y];
4729   int next_element = EL_UNDEFINED;
4730
4731   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4732     return;
4733
4734   if (IS_MOVING(x, y))
4735   {
4736     Moving2Blocked(x, y, &newx, &newy);
4737
4738     if (Feld[newx][newy] != EL_BLOCKED)
4739     {
4740       /* element is moving, but target field is not free (blocked), but
4741          already occupied by something different (example: acid pool);
4742          in this case, only remove the moving field, but not the target */
4743
4744       RemoveField(oldx, oldy);
4745
4746       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4747
4748       TEST_DrawLevelField(oldx, oldy);
4749
4750       return;
4751     }
4752   }
4753   else if (element == EL_BLOCKED)
4754   {
4755     Blocked2Moving(x, y, &oldx, &oldy);
4756     if (!IS_MOVING(oldx, oldy))
4757       return;
4758   }
4759
4760   if (element == EL_BLOCKED &&
4761       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4762        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4763        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4764        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4765        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4766        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4767     next_element = get_next_element(Feld[oldx][oldy]);
4768
4769   RemoveField(oldx, oldy);
4770   RemoveField(newx, newy);
4771
4772   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4773
4774   if (next_element != EL_UNDEFINED)
4775     Feld[oldx][oldy] = next_element;
4776
4777   TEST_DrawLevelField(oldx, oldy);
4778   TEST_DrawLevelField(newx, newy);
4779 }
4780
4781 void DrawDynamite(int x, int y)
4782 {
4783   int sx = SCREENX(x), sy = SCREENY(y);
4784   int graphic = el2img(Feld[x][y]);
4785   int frame;
4786
4787   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4788     return;
4789
4790   if (IS_WALKABLE_INSIDE(Back[x][y]))
4791     return;
4792
4793   if (Back[x][y])
4794     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4795   else if (Store[x][y])
4796     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4797
4798   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4799
4800   if (Back[x][y] || Store[x][y])
4801     DrawGraphicThruMask(sx, sy, graphic, frame);
4802   else
4803     DrawGraphic(sx, sy, graphic, frame);
4804 }
4805
4806 void CheckDynamite(int x, int y)
4807 {
4808   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4809   {
4810     MovDelay[x][y]--;
4811
4812     if (MovDelay[x][y] != 0)
4813     {
4814       DrawDynamite(x, y);
4815       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4816
4817       return;
4818     }
4819   }
4820
4821   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4822
4823   Bang(x, y);
4824 }
4825
4826 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4827 {
4828   boolean num_checked_players = 0;
4829   int i;
4830
4831   for (i = 0; i < MAX_PLAYERS; i++)
4832   {
4833     if (stored_player[i].active)
4834     {
4835       int sx = stored_player[i].jx;
4836       int sy = stored_player[i].jy;
4837
4838       if (num_checked_players == 0)
4839       {
4840         *sx1 = *sx2 = sx;
4841         *sy1 = *sy2 = sy;
4842       }
4843       else
4844       {
4845         *sx1 = MIN(*sx1, sx);
4846         *sy1 = MIN(*sy1, sy);
4847         *sx2 = MAX(*sx2, sx);
4848         *sy2 = MAX(*sy2, sy);
4849       }
4850
4851       num_checked_players++;
4852     }
4853   }
4854 }
4855
4856 static boolean checkIfAllPlayersFitToScreen_RND()
4857 {
4858   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4859
4860   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4861
4862   return (sx2 - sx1 < SCR_FIELDX &&
4863           sy2 - sy1 < SCR_FIELDY);
4864 }
4865
4866 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4867 {
4868   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4869
4870   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4871
4872   *sx = (sx1 + sx2) / 2;
4873   *sy = (sy1 + sy2) / 2;
4874 }
4875
4876 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4877                         boolean center_screen, boolean quick_relocation)
4878 {
4879   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4880   boolean no_delay = (tape.warp_forward);
4881   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4882   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4883
4884   if (quick_relocation)
4885   {
4886     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4887     {
4888       if (!level.shifted_relocation || center_screen)
4889       {
4890         /* quick relocation (without scrolling), with centering of screen */
4891
4892         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4893                     x > SBX_Right + MIDPOSX ? SBX_Right :
4894                     x - MIDPOSX);
4895
4896         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4897                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4898                     y - MIDPOSY);
4899       }
4900       else
4901       {
4902         /* quick relocation (without scrolling), but do not center screen */
4903
4904         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4905                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4906                                old_x - MIDPOSX);
4907
4908         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4909                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4910                                old_y - MIDPOSY);
4911
4912         int offset_x = x + (scroll_x - center_scroll_x);
4913         int offset_y = y + (scroll_y - center_scroll_y);
4914
4915         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4916                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4917                     offset_x - MIDPOSX);
4918
4919         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4920                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4921                     offset_y - MIDPOSY);
4922       }
4923     }
4924     else
4925     {
4926       if (!level.shifted_relocation || center_screen)
4927       {
4928         /* quick relocation (without scrolling), with centering of screen */
4929
4930         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4931                     x > SBX_Right + MIDPOSX ? SBX_Right :
4932                     x - MIDPOSX);
4933
4934         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4935                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4936                     y - MIDPOSY);
4937       }
4938       else
4939       {
4940         /* quick relocation (without scrolling), but do not center screen */
4941
4942         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4943                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4944                                old_x - MIDPOSX);
4945
4946         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4947                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4948                                old_y - MIDPOSY);
4949
4950         int offset_x = x + (scroll_x - center_scroll_x);
4951         int offset_y = y + (scroll_y - center_scroll_y);
4952
4953         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4954                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4955                     offset_x - MIDPOSX);
4956
4957         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4958                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4959                     offset_y - MIDPOSY);
4960       }
4961     }
4962
4963     RedrawPlayfield(TRUE, 0,0,0,0);
4964   }
4965   else
4966   {
4967     int scroll_xx, scroll_yy;
4968
4969     if (!level.shifted_relocation || center_screen)
4970     {
4971       /* visible relocation (with scrolling), with centering of screen */
4972
4973       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4974                    x > SBX_Right + MIDPOSX ? SBX_Right :
4975                    x - MIDPOSX);
4976
4977       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4978                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4979                    y - MIDPOSY);
4980     }
4981     else
4982     {
4983       /* visible relocation (with scrolling), but do not center screen */
4984
4985       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4986                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4987                              old_x - MIDPOSX);
4988
4989       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4990                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4991                              old_y - MIDPOSY);
4992
4993       int offset_x = x + (scroll_x - center_scroll_x);
4994       int offset_y = y + (scroll_y - center_scroll_y);
4995
4996       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4997                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4998                    offset_x - MIDPOSX);
4999
5000       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5001                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5002                    offset_y - MIDPOSY);
5003     }
5004
5005
5006     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5007
5008     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5009     {
5010       int dx = 0, dy = 0;
5011       int fx = FX, fy = FY;
5012
5013       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5014       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5015
5016       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5017         break;
5018
5019       scroll_x -= dx;
5020       scroll_y -= dy;
5021
5022       fx += dx * TILEX / 2;
5023       fy += dy * TILEY / 2;
5024
5025       ScrollLevel(dx, dy);
5026       DrawAllPlayers();
5027
5028       /* scroll in two steps of half tile size to make things smoother */
5029       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5030       Delay(wait_delay_value);
5031
5032       /* scroll second step to align at full tile size */
5033       BackToFront();
5034       Delay(wait_delay_value);
5035     }
5036
5037     DrawAllPlayers();
5038     BackToFront();
5039     Delay(wait_delay_value);
5040   }
5041 }
5042
5043 void RelocatePlayer(int jx, int jy, int el_player_raw)
5044 {
5045   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5046   int player_nr = GET_PLAYER_NR(el_player);
5047   struct PlayerInfo *player = &stored_player[player_nr];
5048   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5049   boolean no_delay = (tape.warp_forward);
5050   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5051   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5052   int old_jx = player->jx;
5053   int old_jy = player->jy;
5054   int old_element = Feld[old_jx][old_jy];
5055   int element = Feld[jx][jy];
5056   boolean player_relocated = (old_jx != jx || old_jy != jy);
5057
5058   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5059   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5060   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5061   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5062   int leave_side_horiz = move_dir_horiz;
5063   int leave_side_vert  = move_dir_vert;
5064   int enter_side = enter_side_horiz | enter_side_vert;
5065   int leave_side = leave_side_horiz | leave_side_vert;
5066
5067   if (player->GameOver)         /* do not reanimate dead player */
5068     return;
5069
5070   if (!player_relocated)        /* no need to relocate the player */
5071     return;
5072
5073   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5074   {
5075     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5076     DrawLevelField(jx, jy);
5077   }
5078
5079   if (player->present)
5080   {
5081     while (player->MovPos)
5082     {
5083       ScrollPlayer(player, SCROLL_GO_ON);
5084       ScrollScreen(NULL, SCROLL_GO_ON);
5085
5086       AdvanceFrameAndPlayerCounters(player->index_nr);
5087
5088       DrawPlayer(player);
5089
5090       BackToFront();
5091       Delay(wait_delay_value);
5092     }
5093
5094     DrawPlayer(player);         /* needed here only to cleanup last field */
5095     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5096
5097     player->is_moving = FALSE;
5098   }
5099
5100   if (IS_CUSTOM_ELEMENT(old_element))
5101     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5102                                CE_LEFT_BY_PLAYER,
5103                                player->index_bit, leave_side);
5104
5105   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5106                                       CE_PLAYER_LEAVES_X,
5107                                       player->index_bit, leave_side);
5108
5109   Feld[jx][jy] = el_player;
5110   InitPlayerField(jx, jy, el_player, TRUE);
5111
5112   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5113      possible that the relocation target field did not contain a player element,
5114      but a walkable element, to which the new player was relocated -- in this
5115      case, restore that (already initialized!) element on the player field */
5116   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5117   {
5118     Feld[jx][jy] = element;     /* restore previously existing element */
5119   }
5120
5121   /* only visually relocate centered player */
5122   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5123                      FALSE, level.instant_relocation);
5124
5125   TestIfPlayerTouchesBadThing(jx, jy);
5126   TestIfPlayerTouchesCustomElement(jx, jy);
5127
5128   if (IS_CUSTOM_ELEMENT(element))
5129     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5130                                player->index_bit, enter_side);
5131
5132   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5133                                       player->index_bit, enter_side);
5134
5135   if (player->is_switching)
5136   {
5137     /* ensure that relocation while still switching an element does not cause
5138        a new element to be treated as also switched directly after relocation
5139        (this is important for teleporter switches that teleport the player to
5140        a place where another teleporter switch is in the same direction, which
5141        would then incorrectly be treated as immediately switched before the
5142        direction key that caused the switch was released) */
5143
5144     player->switch_x += jx - old_jx;
5145     player->switch_y += jy - old_jy;
5146   }
5147 }
5148
5149 void Explode(int ex, int ey, int phase, int mode)
5150 {
5151   int x, y;
5152   int last_phase;
5153   int border_element;
5154
5155   /* !!! eliminate this variable !!! */
5156   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5157
5158   if (game.explosions_delayed)
5159   {
5160     ExplodeField[ex][ey] = mode;
5161     return;
5162   }
5163
5164   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5165   {
5166     int center_element = Feld[ex][ey];
5167     int artwork_element, explosion_element;     /* set these values later */
5168
5169     /* remove things displayed in background while burning dynamite */
5170     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5171       Back[ex][ey] = 0;
5172
5173     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5174     {
5175       /* put moving element to center field (and let it explode there) */
5176       center_element = MovingOrBlocked2Element(ex, ey);
5177       RemoveMovingField(ex, ey);
5178       Feld[ex][ey] = center_element;
5179     }
5180
5181     /* now "center_element" is finally determined -- set related values now */
5182     artwork_element = center_element;           /* for custom player artwork */
5183     explosion_element = center_element;         /* for custom player artwork */
5184
5185     if (IS_PLAYER(ex, ey))
5186     {
5187       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5188
5189       artwork_element = stored_player[player_nr].artwork_element;
5190
5191       if (level.use_explosion_element[player_nr])
5192       {
5193         explosion_element = level.explosion_element[player_nr];
5194         artwork_element = explosion_element;
5195       }
5196     }
5197
5198     if (mode == EX_TYPE_NORMAL ||
5199         mode == EX_TYPE_CENTER ||
5200         mode == EX_TYPE_CROSS)
5201       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5202
5203     last_phase = element_info[explosion_element].explosion_delay + 1;
5204
5205     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5206     {
5207       int xx = x - ex + 1;
5208       int yy = y - ey + 1;
5209       int element;
5210
5211       if (!IN_LEV_FIELD(x, y) ||
5212           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5213           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5214         continue;
5215
5216       element = Feld[x][y];
5217
5218       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5219       {
5220         element = MovingOrBlocked2Element(x, y);
5221
5222         if (!IS_EXPLOSION_PROOF(element))
5223           RemoveMovingField(x, y);
5224       }
5225
5226       /* indestructible elements can only explode in center (but not flames) */
5227       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5228                                            mode == EX_TYPE_BORDER)) ||
5229           element == EL_FLAMES)
5230         continue;
5231
5232       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5233          behaviour, for example when touching a yamyam that explodes to rocks
5234          with active deadly shield, a rock is created under the player !!! */
5235       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5236 #if 0
5237       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5238           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5239            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5240 #else
5241       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5242 #endif
5243       {
5244         if (IS_ACTIVE_BOMB(element))
5245         {
5246           /* re-activate things under the bomb like gate or penguin */
5247           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5248           Back[x][y] = 0;
5249         }
5250
5251         continue;
5252       }
5253
5254       /* save walkable background elements while explosion on same tile */
5255       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5256           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5257         Back[x][y] = element;
5258
5259       /* ignite explodable elements reached by other explosion */
5260       if (element == EL_EXPLOSION)
5261         element = Store2[x][y];
5262
5263       if (AmoebaNr[x][y] &&
5264           (element == EL_AMOEBA_FULL ||
5265            element == EL_BD_AMOEBA ||
5266            element == EL_AMOEBA_GROWING))
5267       {
5268         AmoebaCnt[AmoebaNr[x][y]]--;
5269         AmoebaCnt2[AmoebaNr[x][y]]--;
5270       }
5271
5272       RemoveField(x, y);
5273
5274       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5275       {
5276         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5277
5278         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5279
5280         if (PLAYERINFO(ex, ey)->use_murphy)
5281           Store[x][y] = EL_EMPTY;
5282       }
5283
5284       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5285          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5286       else if (ELEM_IS_PLAYER(center_element))
5287         Store[x][y] = EL_EMPTY;
5288       else if (center_element == EL_YAMYAM)
5289         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5290       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5291         Store[x][y] = element_info[center_element].content.e[xx][yy];
5292 #if 1
5293       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5294          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5295          otherwise) -- FIX THIS !!! */
5296       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5297         Store[x][y] = element_info[element].content.e[1][1];
5298 #else
5299       else if (!CAN_EXPLODE(element))
5300         Store[x][y] = element_info[element].content.e[1][1];
5301 #endif
5302       else
5303         Store[x][y] = EL_EMPTY;
5304
5305       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5306           center_element == EL_AMOEBA_TO_DIAMOND)
5307         Store2[x][y] = element;
5308
5309       Feld[x][y] = EL_EXPLOSION;
5310       GfxElement[x][y] = artwork_element;
5311
5312       ExplodePhase[x][y] = 1;
5313       ExplodeDelay[x][y] = last_phase;
5314
5315       Stop[x][y] = TRUE;
5316     }
5317
5318     if (center_element == EL_YAMYAM)
5319       game.yamyam_content_nr =
5320         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5321
5322     return;
5323   }
5324
5325   if (Stop[ex][ey])
5326     return;
5327
5328   x = ex;
5329   y = ey;
5330
5331   if (phase == 1)
5332     GfxFrame[x][y] = 0;         /* restart explosion animation */
5333
5334   last_phase = ExplodeDelay[x][y];
5335
5336   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5337
5338   /* this can happen if the player leaves an explosion just in time */
5339   if (GfxElement[x][y] == EL_UNDEFINED)
5340     GfxElement[x][y] = EL_EMPTY;
5341
5342   border_element = Store2[x][y];
5343   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5344     border_element = StorePlayer[x][y];
5345
5346   if (phase == element_info[border_element].ignition_delay ||
5347       phase == last_phase)
5348   {
5349     boolean border_explosion = FALSE;
5350
5351     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5352         !PLAYER_EXPLOSION_PROTECTED(x, y))
5353     {
5354       KillPlayerUnlessExplosionProtected(x, y);
5355       border_explosion = TRUE;
5356     }
5357     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5358     {
5359       Feld[x][y] = Store2[x][y];
5360       Store2[x][y] = 0;
5361       Bang(x, y);
5362       border_explosion = TRUE;
5363     }
5364     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5365     {
5366       AmoebeUmwandeln(x, y);
5367       Store2[x][y] = 0;
5368       border_explosion = TRUE;
5369     }
5370
5371     /* if an element just explodes due to another explosion (chain-reaction),
5372        do not immediately end the new explosion when it was the last frame of
5373        the explosion (as it would be done in the following "if"-statement!) */
5374     if (border_explosion && phase == last_phase)
5375       return;
5376   }
5377
5378   if (phase == last_phase)
5379   {
5380     int element;
5381
5382     element = Feld[x][y] = Store[x][y];
5383     Store[x][y] = Store2[x][y] = 0;
5384     GfxElement[x][y] = EL_UNDEFINED;
5385
5386     /* player can escape from explosions and might therefore be still alive */
5387     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5388         element <= EL_PLAYER_IS_EXPLODING_4)
5389     {
5390       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5391       int explosion_element = EL_PLAYER_1 + player_nr;
5392       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5393       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5394
5395       if (level.use_explosion_element[player_nr])
5396         explosion_element = level.explosion_element[player_nr];
5397
5398       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5399                     element_info[explosion_element].content.e[xx][yy]);
5400     }
5401
5402     /* restore probably existing indestructible background element */
5403     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5404       element = Feld[x][y] = Back[x][y];
5405     Back[x][y] = 0;
5406
5407     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5408     GfxDir[x][y] = MV_NONE;
5409     ChangeDelay[x][y] = 0;
5410     ChangePage[x][y] = -1;
5411
5412     CustomValue[x][y] = 0;
5413
5414     InitField_WithBug2(x, y, FALSE);
5415
5416     TEST_DrawLevelField(x, y);
5417
5418     TestIfElementTouchesCustomElement(x, y);
5419
5420     if (GFX_CRUMBLED(element))
5421       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5422
5423     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5424       StorePlayer[x][y] = 0;
5425
5426     if (ELEM_IS_PLAYER(element))
5427       RelocatePlayer(x, y, element);
5428   }
5429   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5430   {
5431     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5432     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5433
5434     if (phase == delay)
5435       TEST_DrawLevelFieldCrumbled(x, y);
5436
5437     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5438     {
5439       DrawLevelElement(x, y, Back[x][y]);
5440       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5441     }
5442     else if (IS_WALKABLE_UNDER(Back[x][y]))
5443     {
5444       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5445       DrawLevelElementThruMask(x, y, Back[x][y]);
5446     }
5447     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5448       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5449   }
5450 }
5451
5452 void DynaExplode(int ex, int ey)
5453 {
5454   int i, j;
5455   int dynabomb_element = Feld[ex][ey];
5456   int dynabomb_size = 1;
5457   boolean dynabomb_xl = FALSE;
5458   struct PlayerInfo *player;
5459   static int xy[4][2] =
5460   {
5461     { 0, -1 },
5462     { -1, 0 },
5463     { +1, 0 },
5464     { 0, +1 }
5465   };
5466
5467   if (IS_ACTIVE_BOMB(dynabomb_element))
5468   {
5469     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5470     dynabomb_size = player->dynabomb_size;
5471     dynabomb_xl = player->dynabomb_xl;
5472     player->dynabombs_left++;
5473   }
5474
5475   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5476
5477   for (i = 0; i < NUM_DIRECTIONS; i++)
5478   {
5479     for (j = 1; j <= dynabomb_size; j++)
5480     {
5481       int x = ex + j * xy[i][0];
5482       int y = ey + j * xy[i][1];
5483       int element;
5484
5485       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5486         break;
5487
5488       element = Feld[x][y];
5489
5490       /* do not restart explosions of fields with active bombs */
5491       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5492         continue;
5493
5494       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5495
5496       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5497           !IS_DIGGABLE(element) && !dynabomb_xl)
5498         break;
5499     }
5500   }
5501 }
5502
5503 void Bang(int x, int y)
5504 {
5505   int element = MovingOrBlocked2Element(x, y);
5506   int explosion_type = EX_TYPE_NORMAL;
5507
5508   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5509   {
5510     struct PlayerInfo *player = PLAYERINFO(x, y);
5511
5512     element = Feld[x][y] = player->initial_element;
5513
5514     if (level.use_explosion_element[player->index_nr])
5515     {
5516       int explosion_element = level.explosion_element[player->index_nr];
5517
5518       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5519         explosion_type = EX_TYPE_CROSS;
5520       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5521         explosion_type = EX_TYPE_CENTER;
5522     }
5523   }
5524
5525   switch (element)
5526   {
5527     case EL_BUG:
5528     case EL_SPACESHIP:
5529     case EL_BD_BUTTERFLY:
5530     case EL_BD_FIREFLY:
5531     case EL_YAMYAM:
5532     case EL_DARK_YAMYAM:
5533     case EL_ROBOT:
5534     case EL_PACMAN:
5535     case EL_MOLE:
5536       RaiseScoreElement(element);
5537       break;
5538
5539     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5540     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5541     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5542     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5543     case EL_DYNABOMB_INCREASE_NUMBER:
5544     case EL_DYNABOMB_INCREASE_SIZE:
5545     case EL_DYNABOMB_INCREASE_POWER:
5546       explosion_type = EX_TYPE_DYNA;
5547       break;
5548
5549     case EL_DC_LANDMINE:
5550       explosion_type = EX_TYPE_CENTER;
5551       break;
5552
5553     case EL_PENGUIN:
5554     case EL_LAMP:
5555     case EL_LAMP_ACTIVE:
5556     case EL_AMOEBA_TO_DIAMOND:
5557       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5558         explosion_type = EX_TYPE_CENTER;
5559       break;
5560
5561     default:
5562       if (element_info[element].explosion_type == EXPLODES_CROSS)
5563         explosion_type = EX_TYPE_CROSS;
5564       else if (element_info[element].explosion_type == EXPLODES_1X1)
5565         explosion_type = EX_TYPE_CENTER;
5566       break;
5567   }
5568
5569   if (explosion_type == EX_TYPE_DYNA)
5570     DynaExplode(x, y);
5571   else
5572     Explode(x, y, EX_PHASE_START, explosion_type);
5573
5574   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5575 }
5576
5577 void SplashAcid(int x, int y)
5578 {
5579   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5580       (!IN_LEV_FIELD(x - 1, y - 2) ||
5581        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5582     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5583
5584   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5585       (!IN_LEV_FIELD(x + 1, y - 2) ||
5586        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5587     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5588
5589   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5590 }
5591
5592 static void InitBeltMovement()
5593 {
5594   static int belt_base_element[4] =
5595   {
5596     EL_CONVEYOR_BELT_1_LEFT,
5597     EL_CONVEYOR_BELT_2_LEFT,
5598     EL_CONVEYOR_BELT_3_LEFT,
5599     EL_CONVEYOR_BELT_4_LEFT
5600   };
5601   static int belt_base_active_element[4] =
5602   {
5603     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5604     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5605     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5606     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5607   };
5608
5609   int x, y, i, j;
5610
5611   /* set frame order for belt animation graphic according to belt direction */
5612   for (i = 0; i < NUM_BELTS; i++)
5613   {
5614     int belt_nr = i;
5615
5616     for (j = 0; j < NUM_BELT_PARTS; j++)
5617     {
5618       int element = belt_base_active_element[belt_nr] + j;
5619       int graphic_1 = el2img(element);
5620       int graphic_2 = el2panelimg(element);
5621
5622       if (game.belt_dir[i] == MV_LEFT)
5623       {
5624         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5625         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5626       }
5627       else
5628       {
5629         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5630         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5631       }
5632     }
5633   }
5634
5635   SCAN_PLAYFIELD(x, y)
5636   {
5637     int element = Feld[x][y];
5638
5639     for (i = 0; i < NUM_BELTS; i++)
5640     {
5641       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5642       {
5643         int e_belt_nr = getBeltNrFromBeltElement(element);
5644         int belt_nr = i;
5645
5646         if (e_belt_nr == belt_nr)
5647         {
5648           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5649
5650           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5651         }
5652       }
5653     }
5654   }
5655 }
5656
5657 static void ToggleBeltSwitch(int x, int y)
5658 {
5659   static int belt_base_element[4] =
5660   {
5661     EL_CONVEYOR_BELT_1_LEFT,
5662     EL_CONVEYOR_BELT_2_LEFT,
5663     EL_CONVEYOR_BELT_3_LEFT,
5664     EL_CONVEYOR_BELT_4_LEFT
5665   };
5666   static int belt_base_active_element[4] =
5667   {
5668     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5669     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5670     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5671     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5672   };
5673   static int belt_base_switch_element[4] =
5674   {
5675     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5676     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5677     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5678     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5679   };
5680   static int belt_move_dir[4] =
5681   {
5682     MV_LEFT,
5683     MV_NONE,
5684     MV_RIGHT,
5685     MV_NONE,
5686   };
5687
5688   int element = Feld[x][y];
5689   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5690   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5691   int belt_dir = belt_move_dir[belt_dir_nr];
5692   int xx, yy, i;
5693
5694   if (!IS_BELT_SWITCH(element))
5695     return;
5696
5697   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5698   game.belt_dir[belt_nr] = belt_dir;
5699
5700   if (belt_dir_nr == 3)
5701     belt_dir_nr = 1;
5702
5703   /* set frame order for belt animation graphic according to belt direction */
5704   for (i = 0; i < NUM_BELT_PARTS; i++)
5705   {
5706     int element = belt_base_active_element[belt_nr] + i;
5707     int graphic_1 = el2img(element);
5708     int graphic_2 = el2panelimg(element);
5709
5710     if (belt_dir == MV_LEFT)
5711     {
5712       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5713       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5714     }
5715     else
5716     {
5717       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5718       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5719     }
5720   }
5721
5722   SCAN_PLAYFIELD(xx, yy)
5723   {
5724     int element = Feld[xx][yy];
5725
5726     if (IS_BELT_SWITCH(element))
5727     {
5728       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5729
5730       if (e_belt_nr == belt_nr)
5731       {
5732         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5733         TEST_DrawLevelField(xx, yy);
5734       }
5735     }
5736     else if (IS_BELT(element) && belt_dir != MV_NONE)
5737     {
5738       int e_belt_nr = getBeltNrFromBeltElement(element);
5739
5740       if (e_belt_nr == belt_nr)
5741       {
5742         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5743
5744         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5745         TEST_DrawLevelField(xx, yy);
5746       }
5747     }
5748     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5749     {
5750       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5751
5752       if (e_belt_nr == belt_nr)
5753       {
5754         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5755
5756         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5757         TEST_DrawLevelField(xx, yy);
5758       }
5759     }
5760   }
5761 }
5762
5763 static void ToggleSwitchgateSwitch(int x, int y)
5764 {
5765   int xx, yy;
5766
5767   game.switchgate_pos = !game.switchgate_pos;
5768
5769   SCAN_PLAYFIELD(xx, yy)
5770   {
5771     int element = Feld[xx][yy];
5772
5773     if (element == EL_SWITCHGATE_SWITCH_UP)
5774     {
5775       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5776       TEST_DrawLevelField(xx, yy);
5777     }
5778     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5779     {
5780       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5781       TEST_DrawLevelField(xx, yy);
5782     }
5783     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5784     {
5785       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5786       TEST_DrawLevelField(xx, yy);
5787     }
5788     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5789     {
5790       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5791       TEST_DrawLevelField(xx, yy);
5792     }
5793     else if (element == EL_SWITCHGATE_OPEN ||
5794              element == EL_SWITCHGATE_OPENING)
5795     {
5796       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5797
5798       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5799     }
5800     else if (element == EL_SWITCHGATE_CLOSED ||
5801              element == EL_SWITCHGATE_CLOSING)
5802     {
5803       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5804
5805       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5806     }
5807   }
5808 }
5809
5810 static int getInvisibleActiveFromInvisibleElement(int element)
5811 {
5812   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5813           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5814           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5815           element);
5816 }
5817
5818 static int getInvisibleFromInvisibleActiveElement(int element)
5819 {
5820   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5821           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5822           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5823           element);
5824 }
5825
5826 static void RedrawAllLightSwitchesAndInvisibleElements()
5827 {
5828   int x, y;
5829
5830   SCAN_PLAYFIELD(x, y)
5831   {
5832     int element = Feld[x][y];
5833
5834     if (element == EL_LIGHT_SWITCH &&
5835         game.light_time_left > 0)
5836     {
5837       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5838       TEST_DrawLevelField(x, y);
5839     }
5840     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5841              game.light_time_left == 0)
5842     {
5843       Feld[x][y] = EL_LIGHT_SWITCH;
5844       TEST_DrawLevelField(x, y);
5845     }
5846     else if (element == EL_EMC_DRIPPER &&
5847              game.light_time_left > 0)
5848     {
5849       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5850       TEST_DrawLevelField(x, y);
5851     }
5852     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5853              game.light_time_left == 0)
5854     {
5855       Feld[x][y] = EL_EMC_DRIPPER;
5856       TEST_DrawLevelField(x, y);
5857     }
5858     else if (element == EL_INVISIBLE_STEELWALL ||
5859              element == EL_INVISIBLE_WALL ||
5860              element == EL_INVISIBLE_SAND)
5861     {
5862       if (game.light_time_left > 0)
5863         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5864
5865       TEST_DrawLevelField(x, y);
5866
5867       /* uncrumble neighbour fields, if needed */
5868       if (element == EL_INVISIBLE_SAND)
5869         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5870     }
5871     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5872              element == EL_INVISIBLE_WALL_ACTIVE ||
5873              element == EL_INVISIBLE_SAND_ACTIVE)
5874     {
5875       if (game.light_time_left == 0)
5876         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5877
5878       TEST_DrawLevelField(x, y);
5879
5880       /* re-crumble neighbour fields, if needed */
5881       if (element == EL_INVISIBLE_SAND)
5882         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5883     }
5884   }
5885 }
5886
5887 static void RedrawAllInvisibleElementsForLenses()
5888 {
5889   int x, y;
5890
5891   SCAN_PLAYFIELD(x, y)
5892   {
5893     int element = Feld[x][y];
5894
5895     if (element == EL_EMC_DRIPPER &&
5896         game.lenses_time_left > 0)
5897     {
5898       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5899       TEST_DrawLevelField(x, y);
5900     }
5901     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5902              game.lenses_time_left == 0)
5903     {
5904       Feld[x][y] = EL_EMC_DRIPPER;
5905       TEST_DrawLevelField(x, y);
5906     }
5907     else if (element == EL_INVISIBLE_STEELWALL ||
5908              element == EL_INVISIBLE_WALL ||
5909              element == EL_INVISIBLE_SAND)
5910     {
5911       if (game.lenses_time_left > 0)
5912         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5913
5914       TEST_DrawLevelField(x, y);
5915
5916       /* uncrumble neighbour fields, if needed */
5917       if (element == EL_INVISIBLE_SAND)
5918         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5919     }
5920     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5921              element == EL_INVISIBLE_WALL_ACTIVE ||
5922              element == EL_INVISIBLE_SAND_ACTIVE)
5923     {
5924       if (game.lenses_time_left == 0)
5925         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5926
5927       TEST_DrawLevelField(x, y);
5928
5929       /* re-crumble neighbour fields, if needed */
5930       if (element == EL_INVISIBLE_SAND)
5931         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5932     }
5933   }
5934 }
5935
5936 static void RedrawAllInvisibleElementsForMagnifier()
5937 {
5938   int x, y;
5939
5940   SCAN_PLAYFIELD(x, y)
5941   {
5942     int element = Feld[x][y];
5943
5944     if (element == EL_EMC_FAKE_GRASS &&
5945         game.magnify_time_left > 0)
5946     {
5947       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5948       TEST_DrawLevelField(x, y);
5949     }
5950     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5951              game.magnify_time_left == 0)
5952     {
5953       Feld[x][y] = EL_EMC_FAKE_GRASS;
5954       TEST_DrawLevelField(x, y);
5955     }
5956     else if (IS_GATE_GRAY(element) &&
5957              game.magnify_time_left > 0)
5958     {
5959       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5960                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5961                     IS_EM_GATE_GRAY(element) ?
5962                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5963                     IS_EMC_GATE_GRAY(element) ?
5964                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5965                     IS_DC_GATE_GRAY(element) ?
5966                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5967                     element);
5968       TEST_DrawLevelField(x, y);
5969     }
5970     else if (IS_GATE_GRAY_ACTIVE(element) &&
5971              game.magnify_time_left == 0)
5972     {
5973       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5974                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5975                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5976                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5977                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5978                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5979                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5980                     EL_DC_GATE_WHITE_GRAY :
5981                     element);
5982       TEST_DrawLevelField(x, y);
5983     }
5984   }
5985 }
5986
5987 static void ToggleLightSwitch(int x, int y)
5988 {
5989   int element = Feld[x][y];
5990
5991   game.light_time_left =
5992     (element == EL_LIGHT_SWITCH ?
5993      level.time_light * FRAMES_PER_SECOND : 0);
5994
5995   RedrawAllLightSwitchesAndInvisibleElements();
5996 }
5997
5998 static void ActivateTimegateSwitch(int x, int y)
5999 {
6000   int xx, yy;
6001
6002   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6003
6004   SCAN_PLAYFIELD(xx, yy)
6005   {
6006     int element = Feld[xx][yy];
6007
6008     if (element == EL_TIMEGATE_CLOSED ||
6009         element == EL_TIMEGATE_CLOSING)
6010     {
6011       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6012       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6013     }
6014
6015     /*
6016     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6017     {
6018       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6019       TEST_DrawLevelField(xx, yy);
6020     }
6021     */
6022
6023   }
6024
6025   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6026                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6027 }
6028
6029 void Impact(int x, int y)
6030 {
6031   boolean last_line = (y == lev_fieldy - 1);
6032   boolean object_hit = FALSE;
6033   boolean impact = (last_line || object_hit);
6034   int element = Feld[x][y];
6035   int smashed = EL_STEELWALL;
6036
6037   if (!last_line)       /* check if element below was hit */
6038   {
6039     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6040       return;
6041
6042     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6043                                          MovDir[x][y + 1] != MV_DOWN ||
6044                                          MovPos[x][y + 1] <= TILEY / 2));
6045
6046     /* do not smash moving elements that left the smashed field in time */
6047     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6048         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6049       object_hit = FALSE;
6050
6051 #if USE_QUICKSAND_IMPACT_BUGFIX
6052     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6053     {
6054       RemoveMovingField(x, y + 1);
6055       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6056       Feld[x][y + 2] = EL_ROCK;
6057       TEST_DrawLevelField(x, y + 2);
6058
6059       object_hit = TRUE;
6060     }
6061
6062     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6063     {
6064       RemoveMovingField(x, y + 1);
6065       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6066       Feld[x][y + 2] = EL_ROCK;
6067       TEST_DrawLevelField(x, y + 2);
6068
6069       object_hit = TRUE;
6070     }
6071 #endif
6072
6073     if (object_hit)
6074       smashed = MovingOrBlocked2Element(x, y + 1);
6075
6076     impact = (last_line || object_hit);
6077   }
6078
6079   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6080   {
6081     SplashAcid(x, y + 1);
6082     return;
6083   }
6084
6085   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6086   /* only reset graphic animation if graphic really changes after impact */
6087   if (impact &&
6088       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6089   {
6090     ResetGfxAnimation(x, y);
6091     TEST_DrawLevelField(x, y);
6092   }
6093
6094   if (impact && CAN_EXPLODE_IMPACT(element))
6095   {
6096     Bang(x, y);
6097     return;
6098   }
6099   else if (impact && element == EL_PEARL &&
6100            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6101   {
6102     ResetGfxAnimation(x, y);
6103
6104     Feld[x][y] = EL_PEARL_BREAKING;
6105     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6106     return;
6107   }
6108   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6109   {
6110     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6111
6112     return;
6113   }
6114
6115   if (impact && element == EL_AMOEBA_DROP)
6116   {
6117     if (object_hit && IS_PLAYER(x, y + 1))
6118       KillPlayerUnlessEnemyProtected(x, y + 1);
6119     else if (object_hit && smashed == EL_PENGUIN)
6120       Bang(x, y + 1);
6121     else
6122     {
6123       Feld[x][y] = EL_AMOEBA_GROWING;
6124       Store[x][y] = EL_AMOEBA_WET;
6125
6126       ResetRandomAnimationValue(x, y);
6127     }
6128     return;
6129   }
6130
6131   if (object_hit)               /* check which object was hit */
6132   {
6133     if ((CAN_PASS_MAGIC_WALL(element) && 
6134          (smashed == EL_MAGIC_WALL ||
6135           smashed == EL_BD_MAGIC_WALL)) ||
6136         (CAN_PASS_DC_MAGIC_WALL(element) &&
6137          smashed == EL_DC_MAGIC_WALL))
6138     {
6139       int xx, yy;
6140       int activated_magic_wall =
6141         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6142          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6143          EL_DC_MAGIC_WALL_ACTIVE);
6144
6145       /* activate magic wall / mill */
6146       SCAN_PLAYFIELD(xx, yy)
6147       {
6148         if (Feld[xx][yy] == smashed)
6149           Feld[xx][yy] = activated_magic_wall;
6150       }
6151
6152       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6153       game.magic_wall_active = TRUE;
6154
6155       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6156                             SND_MAGIC_WALL_ACTIVATING :
6157                             smashed == EL_BD_MAGIC_WALL ?
6158                             SND_BD_MAGIC_WALL_ACTIVATING :
6159                             SND_DC_MAGIC_WALL_ACTIVATING));
6160     }
6161
6162     if (IS_PLAYER(x, y + 1))
6163     {
6164       if (CAN_SMASH_PLAYER(element))
6165       {
6166         KillPlayerUnlessEnemyProtected(x, y + 1);
6167         return;
6168       }
6169     }
6170     else if (smashed == EL_PENGUIN)
6171     {
6172       if (CAN_SMASH_PLAYER(element))
6173       {
6174         Bang(x, y + 1);
6175         return;
6176       }
6177     }
6178     else if (element == EL_BD_DIAMOND)
6179     {
6180       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6181       {
6182         Bang(x, y + 1);
6183         return;
6184       }
6185     }
6186     else if (((element == EL_SP_INFOTRON ||
6187                element == EL_SP_ZONK) &&
6188               (smashed == EL_SP_SNIKSNAK ||
6189                smashed == EL_SP_ELECTRON ||
6190                smashed == EL_SP_DISK_ORANGE)) ||
6191              (element == EL_SP_INFOTRON &&
6192               smashed == EL_SP_DISK_YELLOW))
6193     {
6194       Bang(x, y + 1);
6195       return;
6196     }
6197     else if (CAN_SMASH_EVERYTHING(element))
6198     {
6199       if (IS_CLASSIC_ENEMY(smashed) ||
6200           CAN_EXPLODE_SMASHED(smashed))
6201       {
6202         Bang(x, y + 1);
6203         return;
6204       }
6205       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6206       {
6207         if (smashed == EL_LAMP ||
6208             smashed == EL_LAMP_ACTIVE)
6209         {
6210           Bang(x, y + 1);
6211           return;
6212         }
6213         else if (smashed == EL_NUT)
6214         {
6215           Feld[x][y + 1] = EL_NUT_BREAKING;
6216           PlayLevelSound(x, y, SND_NUT_BREAKING);
6217           RaiseScoreElement(EL_NUT);
6218           return;
6219         }
6220         else if (smashed == EL_PEARL)
6221         {
6222           ResetGfxAnimation(x, y);
6223
6224           Feld[x][y + 1] = EL_PEARL_BREAKING;
6225           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6226           return;
6227         }
6228         else if (smashed == EL_DIAMOND)
6229         {
6230           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6231           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6232           return;
6233         }
6234         else if (IS_BELT_SWITCH(smashed))
6235         {
6236           ToggleBeltSwitch(x, y + 1);
6237         }
6238         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6239                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6240                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6241                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6242         {
6243           ToggleSwitchgateSwitch(x, y + 1);
6244         }
6245         else if (smashed == EL_LIGHT_SWITCH ||
6246                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6247         {
6248           ToggleLightSwitch(x, y + 1);
6249         }
6250         else
6251         {
6252           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6253
6254           CheckElementChangeBySide(x, y + 1, smashed, element,
6255                                    CE_SWITCHED, CH_SIDE_TOP);
6256           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6257                                             CH_SIDE_TOP);
6258         }
6259       }
6260       else
6261       {
6262         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6263       }
6264     }
6265   }
6266
6267   /* play sound of magic wall / mill */
6268   if (!last_line &&
6269       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6270        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6271        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6272   {
6273     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6274       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6275     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6276       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6277     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6278       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6279
6280     return;
6281   }
6282
6283   /* play sound of object that hits the ground */
6284   if (last_line || object_hit)
6285     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6286 }
6287
6288 inline static void TurnRoundExt(int x, int y)
6289 {
6290   static struct
6291   {
6292     int dx, dy;
6293   } move_xy[] =
6294   {
6295     {  0,  0 },
6296     { -1,  0 },
6297     { +1,  0 },
6298     {  0,  0 },
6299     {  0, -1 },
6300     {  0,  0 }, { 0, 0 }, { 0, 0 },
6301     {  0, +1 }
6302   };
6303   static struct
6304   {
6305     int left, right, back;
6306   } turn[] =
6307   {
6308     { 0,        0,              0        },
6309     { MV_DOWN,  MV_UP,          MV_RIGHT },
6310     { MV_UP,    MV_DOWN,        MV_LEFT  },
6311     { 0,        0,              0        },
6312     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6313     { 0,        0,              0        },
6314     { 0,        0,              0        },
6315     { 0,        0,              0        },
6316     { MV_RIGHT, MV_LEFT,        MV_UP    }
6317   };
6318
6319   int element = Feld[x][y];
6320   int move_pattern = element_info[element].move_pattern;
6321
6322   int old_move_dir = MovDir[x][y];
6323   int left_dir  = turn[old_move_dir].left;
6324   int right_dir = turn[old_move_dir].right;
6325   int back_dir  = turn[old_move_dir].back;
6326
6327   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6328   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6329   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6330   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6331
6332   int left_x  = x + left_dx,  left_y  = y + left_dy;
6333   int right_x = x + right_dx, right_y = y + right_dy;
6334   int move_x  = x + move_dx,  move_y  = y + move_dy;
6335
6336   int xx, yy;
6337
6338   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6339   {
6340     TestIfBadThingTouchesOtherBadThing(x, y);
6341
6342     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6343       MovDir[x][y] = right_dir;
6344     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6345       MovDir[x][y] = left_dir;
6346
6347     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6348       MovDelay[x][y] = 9;
6349     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6350       MovDelay[x][y] = 1;
6351   }
6352   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6353   {
6354     TestIfBadThingTouchesOtherBadThing(x, y);
6355
6356     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6357       MovDir[x][y] = left_dir;
6358     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6359       MovDir[x][y] = right_dir;
6360
6361     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6362       MovDelay[x][y] = 9;
6363     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6364       MovDelay[x][y] = 1;
6365   }
6366   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6367   {
6368     TestIfBadThingTouchesOtherBadThing(x, y);
6369
6370     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6371       MovDir[x][y] = left_dir;
6372     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6373       MovDir[x][y] = right_dir;
6374
6375     if (MovDir[x][y] != old_move_dir)
6376       MovDelay[x][y] = 9;
6377   }
6378   else if (element == EL_YAMYAM)
6379   {
6380     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6381     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6382
6383     if (can_turn_left && can_turn_right)
6384       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6385     else if (can_turn_left)
6386       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6387     else if (can_turn_right)
6388       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6389     else
6390       MovDir[x][y] = back_dir;
6391
6392     MovDelay[x][y] = 16 + 16 * RND(3);
6393   }
6394   else if (element == EL_DARK_YAMYAM)
6395   {
6396     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6397                                                          left_x, left_y);
6398     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6399                                                          right_x, right_y);
6400
6401     if (can_turn_left && can_turn_right)
6402       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6403     else if (can_turn_left)
6404       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6405     else if (can_turn_right)
6406       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6407     else
6408       MovDir[x][y] = back_dir;
6409
6410     MovDelay[x][y] = 16 + 16 * RND(3);
6411   }
6412   else if (element == EL_PACMAN)
6413   {
6414     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6415     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6416
6417     if (can_turn_left && can_turn_right)
6418       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6419     else if (can_turn_left)
6420       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6421     else if (can_turn_right)
6422       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6423     else
6424       MovDir[x][y] = back_dir;
6425
6426     MovDelay[x][y] = 6 + RND(40);
6427   }
6428   else if (element == EL_PIG)
6429   {
6430     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6431     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6432     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6433     boolean should_turn_left, should_turn_right, should_move_on;
6434     int rnd_value = 24;
6435     int rnd = RND(rnd_value);
6436
6437     should_turn_left = (can_turn_left &&
6438                         (!can_move_on ||
6439                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6440                                                    y + back_dy + left_dy)));
6441     should_turn_right = (can_turn_right &&
6442                          (!can_move_on ||
6443                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6444                                                     y + back_dy + right_dy)));
6445     should_move_on = (can_move_on &&
6446                       (!can_turn_left ||
6447                        !can_turn_right ||
6448                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6449                                                  y + move_dy + left_dy) ||
6450                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6451                                                  y + move_dy + right_dy)));
6452
6453     if (should_turn_left || should_turn_right || should_move_on)
6454     {
6455       if (should_turn_left && should_turn_right && should_move_on)
6456         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6457                         rnd < 2 * rnd_value / 3 ? right_dir :
6458                         old_move_dir);
6459       else if (should_turn_left && should_turn_right)
6460         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6461       else if (should_turn_left && should_move_on)
6462         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6463       else if (should_turn_right && should_move_on)
6464         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6465       else if (should_turn_left)
6466         MovDir[x][y] = left_dir;
6467       else if (should_turn_right)
6468         MovDir[x][y] = right_dir;
6469       else if (should_move_on)
6470         MovDir[x][y] = old_move_dir;
6471     }
6472     else if (can_move_on && rnd > rnd_value / 8)
6473       MovDir[x][y] = old_move_dir;
6474     else if (can_turn_left && can_turn_right)
6475       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6476     else if (can_turn_left && rnd > rnd_value / 8)
6477       MovDir[x][y] = left_dir;
6478     else if (can_turn_right && rnd > rnd_value/8)
6479       MovDir[x][y] = right_dir;
6480     else
6481       MovDir[x][y] = back_dir;
6482
6483     xx = x + move_xy[MovDir[x][y]].dx;
6484     yy = y + move_xy[MovDir[x][y]].dy;
6485
6486     if (!IN_LEV_FIELD(xx, yy) ||
6487         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6488       MovDir[x][y] = old_move_dir;
6489
6490     MovDelay[x][y] = 0;
6491   }
6492   else if (element == EL_DRAGON)
6493   {
6494     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6495     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6496     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6497     int rnd_value = 24;
6498     int rnd = RND(rnd_value);
6499
6500     if (can_move_on && rnd > rnd_value / 8)
6501       MovDir[x][y] = old_move_dir;
6502     else if (can_turn_left && can_turn_right)
6503       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6504     else if (can_turn_left && rnd > rnd_value / 8)
6505       MovDir[x][y] = left_dir;
6506     else if (can_turn_right && rnd > rnd_value / 8)
6507       MovDir[x][y] = right_dir;
6508     else
6509       MovDir[x][y] = back_dir;
6510
6511     xx = x + move_xy[MovDir[x][y]].dx;
6512     yy = y + move_xy[MovDir[x][y]].dy;
6513
6514     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6515       MovDir[x][y] = old_move_dir;
6516
6517     MovDelay[x][y] = 0;
6518   }
6519   else if (element == EL_MOLE)
6520   {
6521     boolean can_move_on =
6522       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6523                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6524                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6525     if (!can_move_on)
6526     {
6527       boolean can_turn_left =
6528         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6529                               IS_AMOEBOID(Feld[left_x][left_y])));
6530
6531       boolean can_turn_right =
6532         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6533                               IS_AMOEBOID(Feld[right_x][right_y])));
6534
6535       if (can_turn_left && can_turn_right)
6536         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6537       else if (can_turn_left)
6538         MovDir[x][y] = left_dir;
6539       else
6540         MovDir[x][y] = right_dir;
6541     }
6542
6543     if (MovDir[x][y] != old_move_dir)
6544       MovDelay[x][y] = 9;
6545   }
6546   else if (element == EL_BALLOON)
6547   {
6548     MovDir[x][y] = game.wind_direction;
6549     MovDelay[x][y] = 0;
6550   }
6551   else if (element == EL_SPRING)
6552   {
6553     if (MovDir[x][y] & MV_HORIZONTAL)
6554     {
6555       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6556           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6557       {
6558         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6559         ResetGfxAnimation(move_x, move_y);
6560         TEST_DrawLevelField(move_x, move_y);
6561
6562         MovDir[x][y] = back_dir;
6563       }
6564       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6565                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6566         MovDir[x][y] = MV_NONE;
6567     }
6568
6569     MovDelay[x][y] = 0;
6570   }
6571   else if (element == EL_ROBOT ||
6572            element == EL_SATELLITE ||
6573            element == EL_PENGUIN ||
6574            element == EL_EMC_ANDROID)
6575   {
6576     int attr_x = -1, attr_y = -1;
6577
6578     if (AllPlayersGone)
6579     {
6580       attr_x = ExitX;
6581       attr_y = ExitY;
6582     }
6583     else
6584     {
6585       int i;
6586
6587       for (i = 0; i < MAX_PLAYERS; i++)
6588       {
6589         struct PlayerInfo *player = &stored_player[i];
6590         int jx = player->jx, jy = player->jy;
6591
6592         if (!player->active)
6593           continue;
6594
6595         if (attr_x == -1 ||
6596             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6597         {
6598           attr_x = jx;
6599           attr_y = jy;
6600         }
6601       }
6602     }
6603
6604     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6605         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6606          game.engine_version < VERSION_IDENT(3,1,0,0)))
6607     {
6608       attr_x = ZX;
6609       attr_y = ZY;
6610     }
6611
6612     if (element == EL_PENGUIN)
6613     {
6614       int i;
6615       static int xy[4][2] =
6616       {
6617         { 0, -1 },
6618         { -1, 0 },
6619         { +1, 0 },
6620         { 0, +1 }
6621       };
6622
6623       for (i = 0; i < NUM_DIRECTIONS; i++)
6624       {
6625         int ex = x + xy[i][0];
6626         int ey = y + xy[i][1];
6627
6628         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6629                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6630                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6631                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6632         {
6633           attr_x = ex;
6634           attr_y = ey;
6635           break;
6636         }
6637       }
6638     }
6639
6640     MovDir[x][y] = MV_NONE;
6641     if (attr_x < x)
6642       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6643     else if (attr_x > x)
6644       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6645     if (attr_y < y)
6646       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6647     else if (attr_y > y)
6648       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6649
6650     if (element == EL_ROBOT)
6651     {
6652       int newx, newy;
6653
6654       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6655         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6656       Moving2Blocked(x, y, &newx, &newy);
6657
6658       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6659         MovDelay[x][y] = 8 + 8 * !RND(3);
6660       else
6661         MovDelay[x][y] = 16;
6662     }
6663     else if (element == EL_PENGUIN)
6664     {
6665       int newx, newy;
6666
6667       MovDelay[x][y] = 1;
6668
6669       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6670       {
6671         boolean first_horiz = RND(2);
6672         int new_move_dir = MovDir[x][y];
6673
6674         MovDir[x][y] =
6675           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6676         Moving2Blocked(x, y, &newx, &newy);
6677
6678         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6679           return;
6680
6681         MovDir[x][y] =
6682           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6683         Moving2Blocked(x, y, &newx, &newy);
6684
6685         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6686           return;
6687
6688         MovDir[x][y] = old_move_dir;
6689         return;
6690       }
6691     }
6692     else if (element == EL_SATELLITE)
6693     {
6694       int newx, newy;
6695
6696       MovDelay[x][y] = 1;
6697
6698       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6699       {
6700         boolean first_horiz = RND(2);
6701         int new_move_dir = MovDir[x][y];
6702
6703         MovDir[x][y] =
6704           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6705         Moving2Blocked(x, y, &newx, &newy);
6706
6707         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6708           return;
6709
6710         MovDir[x][y] =
6711           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6712         Moving2Blocked(x, y, &newx, &newy);
6713
6714         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6715           return;
6716
6717         MovDir[x][y] = old_move_dir;
6718         return;
6719       }
6720     }
6721     else if (element == EL_EMC_ANDROID)
6722     {
6723       static int check_pos[16] =
6724       {
6725         -1,             /*  0 => (invalid)          */
6726         7,              /*  1 => MV_LEFT            */
6727         3,              /*  2 => MV_RIGHT           */
6728         -1,             /*  3 => (invalid)          */
6729         1,              /*  4 =>            MV_UP   */
6730         0,              /*  5 => MV_LEFT  | MV_UP   */
6731         2,              /*  6 => MV_RIGHT | MV_UP   */
6732         -1,             /*  7 => (invalid)          */
6733         5,              /*  8 =>            MV_DOWN */
6734         6,              /*  9 => MV_LEFT  | MV_DOWN */
6735         4,              /* 10 => MV_RIGHT | MV_DOWN */
6736         -1,             /* 11 => (invalid)          */
6737         -1,             /* 12 => (invalid)          */
6738         -1,             /* 13 => (invalid)          */
6739         -1,             /* 14 => (invalid)          */
6740         -1,             /* 15 => (invalid)          */
6741       };
6742       static struct
6743       {
6744         int dx, dy;
6745         int dir;
6746       } check_xy[8] =
6747       {
6748         { -1, -1,       MV_LEFT  | MV_UP   },
6749         {  0, -1,                  MV_UP   },
6750         { +1, -1,       MV_RIGHT | MV_UP   },
6751         { +1,  0,       MV_RIGHT           },
6752         { +1, +1,       MV_RIGHT | MV_DOWN },
6753         {  0, +1,                  MV_DOWN },
6754         { -1, +1,       MV_LEFT  | MV_DOWN },
6755         { -1,  0,       MV_LEFT            },
6756       };
6757       int start_pos, check_order;
6758       boolean can_clone = FALSE;
6759       int i;
6760
6761       /* check if there is any free field around current position */
6762       for (i = 0; i < 8; i++)
6763       {
6764         int newx = x + check_xy[i].dx;
6765         int newy = y + check_xy[i].dy;
6766
6767         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6768         {
6769           can_clone = TRUE;
6770
6771           break;
6772         }
6773       }
6774
6775       if (can_clone)            /* randomly find an element to clone */
6776       {
6777         can_clone = FALSE;
6778
6779         start_pos = check_pos[RND(8)];
6780         check_order = (RND(2) ? -1 : +1);
6781
6782         for (i = 0; i < 8; i++)
6783         {
6784           int pos_raw = start_pos + i * check_order;
6785           int pos = (pos_raw + 8) % 8;
6786           int newx = x + check_xy[pos].dx;
6787           int newy = y + check_xy[pos].dy;
6788
6789           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6790           {
6791             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6792             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6793
6794             Store[x][y] = Feld[newx][newy];
6795
6796             can_clone = TRUE;
6797
6798             break;
6799           }
6800         }
6801       }
6802
6803       if (can_clone)            /* randomly find a direction to move */
6804       {
6805         can_clone = FALSE;
6806
6807         start_pos = check_pos[RND(8)];
6808         check_order = (RND(2) ? -1 : +1);
6809
6810         for (i = 0; i < 8; i++)
6811         {
6812           int pos_raw = start_pos + i * check_order;
6813           int pos = (pos_raw + 8) % 8;
6814           int newx = x + check_xy[pos].dx;
6815           int newy = y + check_xy[pos].dy;
6816           int new_move_dir = check_xy[pos].dir;
6817
6818           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6819           {
6820             MovDir[x][y] = new_move_dir;
6821             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6822
6823             can_clone = TRUE;
6824
6825             break;
6826           }
6827         }
6828       }
6829
6830       if (can_clone)            /* cloning and moving successful */
6831         return;
6832
6833       /* cannot clone -- try to move towards player */
6834
6835       start_pos = check_pos[MovDir[x][y] & 0x0f];
6836       check_order = (RND(2) ? -1 : +1);
6837
6838       for (i = 0; i < 3; i++)
6839       {
6840         /* first check start_pos, then previous/next or (next/previous) pos */
6841         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6842         int pos = (pos_raw + 8) % 8;
6843         int newx = x + check_xy[pos].dx;
6844         int newy = y + check_xy[pos].dy;
6845         int new_move_dir = check_xy[pos].dir;
6846
6847         if (IS_PLAYER(newx, newy))
6848           break;
6849
6850         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6851         {
6852           MovDir[x][y] = new_move_dir;
6853           MovDelay[x][y] = level.android_move_time * 8 + 1;
6854
6855           break;
6856         }
6857       }
6858     }
6859   }
6860   else if (move_pattern == MV_TURNING_LEFT ||
6861            move_pattern == MV_TURNING_RIGHT ||
6862            move_pattern == MV_TURNING_LEFT_RIGHT ||
6863            move_pattern == MV_TURNING_RIGHT_LEFT ||
6864            move_pattern == MV_TURNING_RANDOM ||
6865            move_pattern == MV_ALL_DIRECTIONS)
6866   {
6867     boolean can_turn_left =
6868       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6869     boolean can_turn_right =
6870       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6871
6872     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6873       return;
6874
6875     if (move_pattern == MV_TURNING_LEFT)
6876       MovDir[x][y] = left_dir;
6877     else if (move_pattern == MV_TURNING_RIGHT)
6878       MovDir[x][y] = right_dir;
6879     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6880       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6881     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6882       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6883     else if (move_pattern == MV_TURNING_RANDOM)
6884       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6885                       can_turn_right && !can_turn_left ? right_dir :
6886                       RND(2) ? left_dir : right_dir);
6887     else if (can_turn_left && can_turn_right)
6888       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6889     else if (can_turn_left)
6890       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6891     else if (can_turn_right)
6892       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6893     else
6894       MovDir[x][y] = back_dir;
6895
6896     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6897   }
6898   else if (move_pattern == MV_HORIZONTAL ||
6899            move_pattern == MV_VERTICAL)
6900   {
6901     if (move_pattern & old_move_dir)
6902       MovDir[x][y] = back_dir;
6903     else if (move_pattern == MV_HORIZONTAL)
6904       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6905     else if (move_pattern == MV_VERTICAL)
6906       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6907
6908     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6909   }
6910   else if (move_pattern & MV_ANY_DIRECTION)
6911   {
6912     MovDir[x][y] = move_pattern;
6913     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914   }
6915   else if (move_pattern & MV_WIND_DIRECTION)
6916   {
6917     MovDir[x][y] = game.wind_direction;
6918     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6919   }
6920   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6921   {
6922     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6923       MovDir[x][y] = left_dir;
6924     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6925       MovDir[x][y] = right_dir;
6926
6927     if (MovDir[x][y] != old_move_dir)
6928       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6929   }
6930   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6931   {
6932     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6933       MovDir[x][y] = right_dir;
6934     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6935       MovDir[x][y] = left_dir;
6936
6937     if (MovDir[x][y] != old_move_dir)
6938       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6939   }
6940   else if (move_pattern == MV_TOWARDS_PLAYER ||
6941            move_pattern == MV_AWAY_FROM_PLAYER)
6942   {
6943     int attr_x = -1, attr_y = -1;
6944     int newx, newy;
6945     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6946
6947     if (AllPlayersGone)
6948     {
6949       attr_x = ExitX;
6950       attr_y = ExitY;
6951     }
6952     else
6953     {
6954       int i;
6955
6956       for (i = 0; i < MAX_PLAYERS; i++)
6957       {
6958         struct PlayerInfo *player = &stored_player[i];
6959         int jx = player->jx, jy = player->jy;
6960
6961         if (!player->active)
6962           continue;
6963
6964         if (attr_x == -1 ||
6965             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6966         {
6967           attr_x = jx;
6968           attr_y = jy;
6969         }
6970       }
6971     }
6972
6973     MovDir[x][y] = MV_NONE;
6974     if (attr_x < x)
6975       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6976     else if (attr_x > x)
6977       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6978     if (attr_y < y)
6979       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6980     else if (attr_y > y)
6981       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6982
6983     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6984
6985     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6986     {
6987       boolean first_horiz = RND(2);
6988       int new_move_dir = MovDir[x][y];
6989
6990       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6991       {
6992         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6993         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6994
6995         return;
6996       }
6997
6998       MovDir[x][y] =
6999         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7000       Moving2Blocked(x, y, &newx, &newy);
7001
7002       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7003         return;
7004
7005       MovDir[x][y] =
7006         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7007       Moving2Blocked(x, y, &newx, &newy);
7008
7009       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7010         return;
7011
7012       MovDir[x][y] = old_move_dir;
7013     }
7014   }
7015   else if (move_pattern == MV_WHEN_PUSHED ||
7016            move_pattern == MV_WHEN_DROPPED)
7017   {
7018     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7019       MovDir[x][y] = MV_NONE;
7020
7021     MovDelay[x][y] = 0;
7022   }
7023   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7024   {
7025     static int test_xy[7][2] =
7026     {
7027       { 0, -1 },
7028       { -1, 0 },
7029       { +1, 0 },
7030       { 0, +1 },
7031       { 0, -1 },
7032       { -1, 0 },
7033       { +1, 0 },
7034     };
7035     static int test_dir[7] =
7036     {
7037       MV_UP,
7038       MV_LEFT,
7039       MV_RIGHT,
7040       MV_DOWN,
7041       MV_UP,
7042       MV_LEFT,
7043       MV_RIGHT,
7044     };
7045     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7046     int move_preference = -1000000;     /* start with very low preference */
7047     int new_move_dir = MV_NONE;
7048     int start_test = RND(4);
7049     int i;
7050
7051     for (i = 0; i < NUM_DIRECTIONS; i++)
7052     {
7053       int move_dir = test_dir[start_test + i];
7054       int move_dir_preference;
7055
7056       xx = x + test_xy[start_test + i][0];
7057       yy = y + test_xy[start_test + i][1];
7058
7059       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7060           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7061       {
7062         new_move_dir = move_dir;
7063
7064         break;
7065       }
7066
7067       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7068         continue;
7069
7070       move_dir_preference = -1 * RunnerVisit[xx][yy];
7071       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7072         move_dir_preference = PlayerVisit[xx][yy];
7073
7074       if (move_dir_preference > move_preference)
7075       {
7076         /* prefer field that has not been visited for the longest time */
7077         move_preference = move_dir_preference;
7078         new_move_dir = move_dir;
7079       }
7080       else if (move_dir_preference == move_preference &&
7081                move_dir == old_move_dir)
7082       {
7083         /* prefer last direction when all directions are preferred equally */
7084         move_preference = move_dir_preference;
7085         new_move_dir = move_dir;
7086       }
7087     }
7088
7089     MovDir[x][y] = new_move_dir;
7090     if (old_move_dir != new_move_dir)
7091       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7092   }
7093 }
7094
7095 static void TurnRound(int x, int y)
7096 {
7097   int direction = MovDir[x][y];
7098
7099   TurnRoundExt(x, y);
7100
7101   GfxDir[x][y] = MovDir[x][y];
7102
7103   if (direction != MovDir[x][y])
7104     GfxFrame[x][y] = 0;
7105
7106   if (MovDelay[x][y])
7107     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7108
7109   ResetGfxFrame(x, y, FALSE);
7110 }
7111
7112 static boolean JustBeingPushed(int x, int y)
7113 {
7114   int i;
7115
7116   for (i = 0; i < MAX_PLAYERS; i++)
7117   {
7118     struct PlayerInfo *player = &stored_player[i];
7119
7120     if (player->active && player->is_pushing && player->MovPos)
7121     {
7122       int next_jx = player->jx + (player->jx - player->last_jx);
7123       int next_jy = player->jy + (player->jy - player->last_jy);
7124
7125       if (x == next_jx && y == next_jy)
7126         return TRUE;
7127     }
7128   }
7129
7130   return FALSE;
7131 }
7132
7133 void StartMoving(int x, int y)
7134 {
7135   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7136   int element = Feld[x][y];
7137
7138   if (Stop[x][y])
7139     return;
7140
7141   if (MovDelay[x][y] == 0)
7142     GfxAction[x][y] = ACTION_DEFAULT;
7143
7144   if (CAN_FALL(element) && y < lev_fieldy - 1)
7145   {
7146     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7147         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7148       if (JustBeingPushed(x, y))
7149         return;
7150
7151     if (element == EL_QUICKSAND_FULL)
7152     {
7153       if (IS_FREE(x, y + 1))
7154       {
7155         InitMovingField(x, y, MV_DOWN);
7156         started_moving = TRUE;
7157
7158         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7159 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7160         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7161           Store[x][y] = EL_ROCK;
7162 #else
7163         Store[x][y] = EL_ROCK;
7164 #endif
7165
7166         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7167       }
7168       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7169       {
7170         if (!MovDelay[x][y])
7171         {
7172           MovDelay[x][y] = TILEY + 1;
7173
7174           ResetGfxAnimation(x, y);
7175           ResetGfxAnimation(x, y + 1);
7176         }
7177
7178         if (MovDelay[x][y])
7179         {
7180           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7181           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7182
7183           MovDelay[x][y]--;
7184           if (MovDelay[x][y])
7185             return;
7186         }
7187
7188         Feld[x][y] = EL_QUICKSAND_EMPTY;
7189         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7190         Store[x][y + 1] = Store[x][y];
7191         Store[x][y] = 0;
7192
7193         PlayLevelSoundAction(x, y, ACTION_FILLING);
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_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_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     }
7223     else if (element == EL_QUICKSAND_FAST_FULL)
7224     {
7225       if (IS_FREE(x, y + 1))
7226       {
7227         InitMovingField(x, y, MV_DOWN);
7228         started_moving = TRUE;
7229
7230         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7231 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7232         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7233           Store[x][y] = EL_ROCK;
7234 #else
7235         Store[x][y] = EL_ROCK;
7236 #endif
7237
7238         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7239       }
7240       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7241       {
7242         if (!MovDelay[x][y])
7243         {
7244           MovDelay[x][y] = TILEY + 1;
7245
7246           ResetGfxAnimation(x, y);
7247           ResetGfxAnimation(x, y + 1);
7248         }
7249
7250         if (MovDelay[x][y])
7251         {
7252           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7253           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7254
7255           MovDelay[x][y]--;
7256           if (MovDelay[x][y])
7257             return;
7258         }
7259
7260         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7261         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7262         Store[x][y + 1] = Store[x][y];
7263         Store[x][y] = 0;
7264
7265         PlayLevelSoundAction(x, y, ACTION_FILLING);
7266       }
7267       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7268       {
7269         if (!MovDelay[x][y])
7270         {
7271           MovDelay[x][y] = TILEY + 1;
7272
7273           ResetGfxAnimation(x, y);
7274           ResetGfxAnimation(x, y + 1);
7275         }
7276
7277         if (MovDelay[x][y])
7278         {
7279           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7280           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7281
7282           MovDelay[x][y]--;
7283           if (MovDelay[x][y])
7284             return;
7285         }
7286
7287         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7288         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7289         Store[x][y + 1] = Store[x][y];
7290         Store[x][y] = 0;
7291
7292         PlayLevelSoundAction(x, y, ACTION_FILLING);
7293       }
7294     }
7295     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7296              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7297     {
7298       InitMovingField(x, y, MV_DOWN);
7299       started_moving = TRUE;
7300
7301       Feld[x][y] = EL_QUICKSAND_FILLING;
7302       Store[x][y] = element;
7303
7304       PlayLevelSoundAction(x, y, ACTION_FILLING);
7305     }
7306     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7307              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7308     {
7309       InitMovingField(x, y, MV_DOWN);
7310       started_moving = TRUE;
7311
7312       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7313       Store[x][y] = element;
7314
7315       PlayLevelSoundAction(x, y, ACTION_FILLING);
7316     }
7317     else if (element == EL_MAGIC_WALL_FULL)
7318     {
7319       if (IS_FREE(x, y + 1))
7320       {
7321         InitMovingField(x, y, MV_DOWN);
7322         started_moving = TRUE;
7323
7324         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7325         Store[x][y] = EL_CHANGED(Store[x][y]);
7326       }
7327       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7328       {
7329         if (!MovDelay[x][y])
7330           MovDelay[x][y] = TILEY / 4 + 1;
7331
7332         if (MovDelay[x][y])
7333         {
7334           MovDelay[x][y]--;
7335           if (MovDelay[x][y])
7336             return;
7337         }
7338
7339         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7340         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7341         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7342         Store[x][y] = 0;
7343       }
7344     }
7345     else if (element == EL_BD_MAGIC_WALL_FULL)
7346     {
7347       if (IS_FREE(x, y + 1))
7348       {
7349         InitMovingField(x, y, MV_DOWN);
7350         started_moving = TRUE;
7351
7352         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7353         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7354       }
7355       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7356       {
7357         if (!MovDelay[x][y])
7358           MovDelay[x][y] = TILEY / 4 + 1;
7359
7360         if (MovDelay[x][y])
7361         {
7362           MovDelay[x][y]--;
7363           if (MovDelay[x][y])
7364             return;
7365         }
7366
7367         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7368         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7369         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7370         Store[x][y] = 0;
7371       }
7372     }
7373     else if (element == EL_DC_MAGIC_WALL_FULL)
7374     {
7375       if (IS_FREE(x, y + 1))
7376       {
7377         InitMovingField(x, y, MV_DOWN);
7378         started_moving = TRUE;
7379
7380         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7381         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7382       }
7383       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7384       {
7385         if (!MovDelay[x][y])
7386           MovDelay[x][y] = TILEY / 4 + 1;
7387
7388         if (MovDelay[x][y])
7389         {
7390           MovDelay[x][y]--;
7391           if (MovDelay[x][y])
7392             return;
7393         }
7394
7395         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7396         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7397         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7398         Store[x][y] = 0;
7399       }
7400     }
7401     else if ((CAN_PASS_MAGIC_WALL(element) &&
7402               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7403                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7404              (CAN_PASS_DC_MAGIC_WALL(element) &&
7405               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7406
7407     {
7408       InitMovingField(x, y, MV_DOWN);
7409       started_moving = TRUE;
7410
7411       Feld[x][y] =
7412         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7413          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7414          EL_DC_MAGIC_WALL_FILLING);
7415       Store[x][y] = element;
7416     }
7417     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7418     {
7419       SplashAcid(x, y + 1);
7420
7421       InitMovingField(x, y, MV_DOWN);
7422       started_moving = TRUE;
7423
7424       Store[x][y] = EL_ACID;
7425     }
7426     else if (
7427              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7428               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7429              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7430               CAN_FALL(element) && WasJustFalling[x][y] &&
7431               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7432
7433              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7434               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7435               (Feld[x][y + 1] == EL_BLOCKED)))
7436     {
7437       /* this is needed for a special case not covered by calling "Impact()"
7438          from "ContinueMoving()": if an element moves to a tile directly below
7439          another element which was just falling on that tile (which was empty
7440          in the previous frame), the falling element above would just stop
7441          instead of smashing the element below (in previous version, the above
7442          element was just checked for "moving" instead of "falling", resulting
7443          in incorrect smashes caused by horizontal movement of the above
7444          element; also, the case of the player being the element to smash was
7445          simply not covered here... :-/ ) */
7446
7447       CheckCollision[x][y] = 0;
7448       CheckImpact[x][y] = 0;
7449
7450       Impact(x, y);
7451     }
7452     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7453     {
7454       if (MovDir[x][y] == MV_NONE)
7455       {
7456         InitMovingField(x, y, MV_DOWN);
7457         started_moving = TRUE;
7458       }
7459     }
7460     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7461     {
7462       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7463         MovDir[x][y] = MV_DOWN;
7464
7465       InitMovingField(x, y, MV_DOWN);
7466       started_moving = TRUE;
7467     }
7468     else if (element == EL_AMOEBA_DROP)
7469     {
7470       Feld[x][y] = EL_AMOEBA_GROWING;
7471       Store[x][y] = EL_AMOEBA_WET;
7472     }
7473     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7474               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7475              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7476              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7477     {
7478       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7479                                 (IS_FREE(x - 1, y + 1) ||
7480                                  Feld[x - 1][y + 1] == EL_ACID));
7481       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7482                                 (IS_FREE(x + 1, y + 1) ||
7483                                  Feld[x + 1][y + 1] == EL_ACID));
7484       boolean can_fall_any  = (can_fall_left || can_fall_right);
7485       boolean can_fall_both = (can_fall_left && can_fall_right);
7486       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7487
7488       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7489       {
7490         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7491           can_fall_right = FALSE;
7492         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7493           can_fall_left = FALSE;
7494         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7495           can_fall_right = FALSE;
7496         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7497           can_fall_left = FALSE;
7498
7499         can_fall_any  = (can_fall_left || can_fall_right);
7500         can_fall_both = FALSE;
7501       }
7502
7503       if (can_fall_both)
7504       {
7505         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7506           can_fall_right = FALSE;       /* slip down on left side */
7507         else
7508           can_fall_left = !(can_fall_right = RND(2));
7509
7510         can_fall_both = FALSE;
7511       }
7512
7513       if (can_fall_any)
7514       {
7515         /* if not determined otherwise, prefer left side for slipping down */
7516         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7517         started_moving = TRUE;
7518       }
7519     }
7520     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7521     {
7522       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7523       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7524       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7525       int belt_dir = game.belt_dir[belt_nr];
7526
7527       if ((belt_dir == MV_LEFT  && left_is_free) ||
7528           (belt_dir == MV_RIGHT && right_is_free))
7529       {
7530         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7531
7532         InitMovingField(x, y, belt_dir);
7533         started_moving = TRUE;
7534
7535         Pushed[x][y] = TRUE;
7536         Pushed[nextx][y] = TRUE;
7537
7538         GfxAction[x][y] = ACTION_DEFAULT;
7539       }
7540       else
7541       {
7542         MovDir[x][y] = 0;       /* if element was moving, stop it */
7543       }
7544     }
7545   }
7546
7547   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7548   if (CAN_MOVE(element) && !started_moving)
7549   {
7550     int move_pattern = element_info[element].move_pattern;
7551     int newx, newy;
7552
7553     Moving2Blocked(x, y, &newx, &newy);
7554
7555     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7556       return;
7557
7558     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7559         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7560     {
7561       WasJustMoving[x][y] = 0;
7562       CheckCollision[x][y] = 0;
7563
7564       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7565
7566       if (Feld[x][y] != element)        /* element has changed */
7567         return;
7568     }
7569
7570     if (!MovDelay[x][y])        /* start new movement phase */
7571     {
7572       /* all objects that can change their move direction after each step
7573          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7574
7575       if (element != EL_YAMYAM &&
7576           element != EL_DARK_YAMYAM &&
7577           element != EL_PACMAN &&
7578           !(move_pattern & MV_ANY_DIRECTION) &&
7579           move_pattern != MV_TURNING_LEFT &&
7580           move_pattern != MV_TURNING_RIGHT &&
7581           move_pattern != MV_TURNING_LEFT_RIGHT &&
7582           move_pattern != MV_TURNING_RIGHT_LEFT &&
7583           move_pattern != MV_TURNING_RANDOM)
7584       {
7585         TurnRound(x, y);
7586
7587         if (MovDelay[x][y] && (element == EL_BUG ||
7588                                element == EL_SPACESHIP ||
7589                                element == EL_SP_SNIKSNAK ||
7590                                element == EL_SP_ELECTRON ||
7591                                element == EL_MOLE))
7592           TEST_DrawLevelField(x, y);
7593       }
7594     }
7595
7596     if (MovDelay[x][y])         /* wait some time before next movement */
7597     {
7598       MovDelay[x][y]--;
7599
7600       if (element == EL_ROBOT ||
7601           element == EL_YAMYAM ||
7602           element == EL_DARK_YAMYAM)
7603       {
7604         DrawLevelElementAnimationIfNeeded(x, y, element);
7605         PlayLevelSoundAction(x, y, ACTION_WAITING);
7606       }
7607       else if (element == EL_SP_ELECTRON)
7608         DrawLevelElementAnimationIfNeeded(x, y, element);
7609       else if (element == EL_DRAGON)
7610       {
7611         int i;
7612         int dir = MovDir[x][y];
7613         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7614         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7615         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7616                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7617                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7618                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7619         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7620
7621         GfxAction[x][y] = ACTION_ATTACKING;
7622
7623         if (IS_PLAYER(x, y))
7624           DrawPlayerField(x, y);
7625         else
7626           TEST_DrawLevelField(x, y);
7627
7628         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7629
7630         for (i = 1; i <= 3; i++)
7631         {
7632           int xx = x + i * dx;
7633           int yy = y + i * dy;
7634           int sx = SCREENX(xx);
7635           int sy = SCREENY(yy);
7636           int flame_graphic = graphic + (i - 1);
7637
7638           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7639             break;
7640
7641           if (MovDelay[x][y])
7642           {
7643             int flamed = MovingOrBlocked2Element(xx, yy);
7644
7645             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7646               Bang(xx, yy);
7647             else
7648               RemoveMovingField(xx, yy);
7649
7650             ChangeDelay[xx][yy] = 0;
7651
7652             Feld[xx][yy] = EL_FLAMES;
7653
7654             if (IN_SCR_FIELD(sx, sy))
7655             {
7656               TEST_DrawLevelFieldCrumbled(xx, yy);
7657               DrawGraphic(sx, sy, flame_graphic, frame);
7658             }
7659           }
7660           else
7661           {
7662             if (Feld[xx][yy] == EL_FLAMES)
7663               Feld[xx][yy] = EL_EMPTY;
7664             TEST_DrawLevelField(xx, yy);
7665           }
7666         }
7667       }
7668
7669       if (MovDelay[x][y])       /* element still has to wait some time */
7670       {
7671         PlayLevelSoundAction(x, y, ACTION_WAITING);
7672
7673         return;
7674       }
7675     }
7676
7677     /* now make next step */
7678
7679     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7680
7681     if (DONT_COLLIDE_WITH(element) &&
7682         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7683         !PLAYER_ENEMY_PROTECTED(newx, newy))
7684     {
7685       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7686
7687       return;
7688     }
7689
7690     else if (CAN_MOVE_INTO_ACID(element) &&
7691              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7692              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7693              (MovDir[x][y] == MV_DOWN ||
7694               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7695     {
7696       SplashAcid(newx, newy);
7697       Store[x][y] = EL_ACID;
7698     }
7699     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7700     {
7701       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7702           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7703           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7704           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7705       {
7706         RemoveField(x, y);
7707         TEST_DrawLevelField(x, y);
7708
7709         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7710         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7711           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7712
7713         local_player->friends_still_needed--;
7714         if (!local_player->friends_still_needed &&
7715             !local_player->GameOver && AllPlayersGone)
7716           PlayerWins(local_player);
7717
7718         return;
7719       }
7720       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7721       {
7722         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7723           TEST_DrawLevelField(newx, newy);
7724         else
7725           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7726       }
7727       else if (!IS_FREE(newx, newy))
7728       {
7729         GfxAction[x][y] = ACTION_WAITING;
7730
7731         if (IS_PLAYER(x, y))
7732           DrawPlayerField(x, y);
7733         else
7734           TEST_DrawLevelField(x, y);
7735
7736         return;
7737       }
7738     }
7739     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7740     {
7741       if (IS_FOOD_PIG(Feld[newx][newy]))
7742       {
7743         if (IS_MOVING(newx, newy))
7744           RemoveMovingField(newx, newy);
7745         else
7746         {
7747           Feld[newx][newy] = EL_EMPTY;
7748           TEST_DrawLevelField(newx, newy);
7749         }
7750
7751         PlayLevelSound(x, y, SND_PIG_DIGGING);
7752       }
7753       else if (!IS_FREE(newx, newy))
7754       {
7755         if (IS_PLAYER(x, y))
7756           DrawPlayerField(x, y);
7757         else
7758           TEST_DrawLevelField(x, y);
7759
7760         return;
7761       }
7762     }
7763     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7764     {
7765       if (Store[x][y] != EL_EMPTY)
7766       {
7767         boolean can_clone = FALSE;
7768         int xx, yy;
7769
7770         /* check if element to clone is still there */
7771         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7772         {
7773           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7774           {
7775             can_clone = TRUE;
7776
7777             break;
7778           }
7779         }
7780
7781         /* cannot clone or target field not free anymore -- do not clone */
7782         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7783           Store[x][y] = EL_EMPTY;
7784       }
7785
7786       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7787       {
7788         if (IS_MV_DIAGONAL(MovDir[x][y]))
7789         {
7790           int diagonal_move_dir = MovDir[x][y];
7791           int stored = Store[x][y];
7792           int change_delay = 8;
7793           int graphic;
7794
7795           /* android is moving diagonally */
7796
7797           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7798
7799           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7800           GfxElement[x][y] = EL_EMC_ANDROID;
7801           GfxAction[x][y] = ACTION_SHRINKING;
7802           GfxDir[x][y] = diagonal_move_dir;
7803           ChangeDelay[x][y] = change_delay;
7804
7805           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7806                                    GfxDir[x][y]);
7807
7808           DrawLevelGraphicAnimation(x, y, graphic);
7809           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7810
7811           if (Feld[newx][newy] == EL_ACID)
7812           {
7813             SplashAcid(newx, newy);
7814
7815             return;
7816           }
7817
7818           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7819
7820           Store[newx][newy] = EL_EMC_ANDROID;
7821           GfxElement[newx][newy] = EL_EMC_ANDROID;
7822           GfxAction[newx][newy] = ACTION_GROWING;
7823           GfxDir[newx][newy] = diagonal_move_dir;
7824           ChangeDelay[newx][newy] = change_delay;
7825
7826           graphic = el_act_dir2img(GfxElement[newx][newy],
7827                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7828
7829           DrawLevelGraphicAnimation(newx, newy, graphic);
7830           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7831
7832           return;
7833         }
7834         else
7835         {
7836           Feld[newx][newy] = EL_EMPTY;
7837           TEST_DrawLevelField(newx, newy);
7838
7839           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7840         }
7841       }
7842       else if (!IS_FREE(newx, newy))
7843       {
7844         return;
7845       }
7846     }
7847     else if (IS_CUSTOM_ELEMENT(element) &&
7848              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7849     {
7850       if (!DigFieldByCE(newx, newy, element))
7851         return;
7852
7853       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7854       {
7855         RunnerVisit[x][y] = FrameCounter;
7856         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7857       }
7858     }
7859     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7860     {
7861       if (!IS_FREE(newx, newy))
7862       {
7863         if (IS_PLAYER(x, y))
7864           DrawPlayerField(x, y);
7865         else
7866           TEST_DrawLevelField(x, y);
7867
7868         return;
7869       }
7870       else
7871       {
7872         boolean wanna_flame = !RND(10);
7873         int dx = newx - x, dy = newy - y;
7874         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7875         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7876         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7877                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7878         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7879                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7880
7881         if ((wanna_flame ||
7882              IS_CLASSIC_ENEMY(element1) ||
7883              IS_CLASSIC_ENEMY(element2)) &&
7884             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7885             element1 != EL_FLAMES && element2 != EL_FLAMES)
7886         {
7887           ResetGfxAnimation(x, y);
7888           GfxAction[x][y] = ACTION_ATTACKING;
7889
7890           if (IS_PLAYER(x, y))
7891             DrawPlayerField(x, y);
7892           else
7893             TEST_DrawLevelField(x, y);
7894
7895           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7896
7897           MovDelay[x][y] = 50;
7898
7899           Feld[newx][newy] = EL_FLAMES;
7900           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7901             Feld[newx1][newy1] = EL_FLAMES;
7902           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7903             Feld[newx2][newy2] = EL_FLAMES;
7904
7905           return;
7906         }
7907       }
7908     }
7909     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7910              Feld[newx][newy] == EL_DIAMOND)
7911     {
7912       if (IS_MOVING(newx, newy))
7913         RemoveMovingField(newx, newy);
7914       else
7915       {
7916         Feld[newx][newy] = EL_EMPTY;
7917         TEST_DrawLevelField(newx, newy);
7918       }
7919
7920       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7921     }
7922     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7923              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7924     {
7925       if (AmoebaNr[newx][newy])
7926       {
7927         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7928         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7929             Feld[newx][newy] == EL_BD_AMOEBA)
7930           AmoebaCnt[AmoebaNr[newx][newy]]--;
7931       }
7932
7933       if (IS_MOVING(newx, newy))
7934       {
7935         RemoveMovingField(newx, newy);
7936       }
7937       else
7938       {
7939         Feld[newx][newy] = EL_EMPTY;
7940         TEST_DrawLevelField(newx, newy);
7941       }
7942
7943       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7944     }
7945     else if ((element == EL_PACMAN || element == EL_MOLE)
7946              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7947     {
7948       if (AmoebaNr[newx][newy])
7949       {
7950         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7951         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7952             Feld[newx][newy] == EL_BD_AMOEBA)
7953           AmoebaCnt[AmoebaNr[newx][newy]]--;
7954       }
7955
7956       if (element == EL_MOLE)
7957       {
7958         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7959         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7960
7961         ResetGfxAnimation(x, y);
7962         GfxAction[x][y] = ACTION_DIGGING;
7963         TEST_DrawLevelField(x, y);
7964
7965         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7966
7967         return;                         /* wait for shrinking amoeba */
7968       }
7969       else      /* element == EL_PACMAN */
7970       {
7971         Feld[newx][newy] = EL_EMPTY;
7972         TEST_DrawLevelField(newx, newy);
7973         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7974       }
7975     }
7976     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7977              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7978               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7979     {
7980       /* wait for shrinking amoeba to completely disappear */
7981       return;
7982     }
7983     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7984     {
7985       /* object was running against a wall */
7986
7987       TurnRound(x, y);
7988
7989       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7990         DrawLevelElementAnimation(x, y, element);
7991
7992       if (DONT_TOUCH(element))
7993         TestIfBadThingTouchesPlayer(x, y);
7994
7995       return;
7996     }
7997
7998     InitMovingField(x, y, MovDir[x][y]);
7999
8000     PlayLevelSoundAction(x, y, ACTION_MOVING);
8001   }
8002
8003   if (MovDir[x][y])
8004     ContinueMoving(x, y);
8005 }
8006
8007 void ContinueMoving(int x, int y)
8008 {
8009   int element = Feld[x][y];
8010   struct ElementInfo *ei = &element_info[element];
8011   int direction = MovDir[x][y];
8012   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8013   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8014   int newx = x + dx, newy = y + dy;
8015   int stored = Store[x][y];
8016   int stored_new = Store[newx][newy];
8017   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8018   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8019   boolean last_line = (newy == lev_fieldy - 1);
8020
8021   MovPos[x][y] += getElementMoveStepsize(x, y);
8022
8023   if (pushed_by_player) /* special case: moving object pushed by player */
8024     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8025
8026   if (ABS(MovPos[x][y]) < TILEX)
8027   {
8028     TEST_DrawLevelField(x, y);
8029
8030     return;     /* element is still moving */
8031   }
8032
8033   /* element reached destination field */
8034
8035   Feld[x][y] = EL_EMPTY;
8036   Feld[newx][newy] = element;
8037   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8038
8039   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8040   {
8041     element = Feld[newx][newy] = EL_ACID;
8042   }
8043   else if (element == EL_MOLE)
8044   {
8045     Feld[x][y] = EL_SAND;
8046
8047     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8048   }
8049   else if (element == EL_QUICKSAND_FILLING)
8050   {
8051     element = Feld[newx][newy] = get_next_element(element);
8052     Store[newx][newy] = Store[x][y];
8053   }
8054   else if (element == EL_QUICKSAND_EMPTYING)
8055   {
8056     Feld[x][y] = get_next_element(element);
8057     element = Feld[newx][newy] = Store[x][y];
8058   }
8059   else if (element == EL_QUICKSAND_FAST_FILLING)
8060   {
8061     element = Feld[newx][newy] = get_next_element(element);
8062     Store[newx][newy] = Store[x][y];
8063   }
8064   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8065   {
8066     Feld[x][y] = get_next_element(element);
8067     element = Feld[newx][newy] = Store[x][y];
8068   }
8069   else if (element == EL_MAGIC_WALL_FILLING)
8070   {
8071     element = Feld[newx][newy] = get_next_element(element);
8072     if (!game.magic_wall_active)
8073       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8074     Store[newx][newy] = Store[x][y];
8075   }
8076   else if (element == EL_MAGIC_WALL_EMPTYING)
8077   {
8078     Feld[x][y] = get_next_element(element);
8079     if (!game.magic_wall_active)
8080       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8081     element = Feld[newx][newy] = Store[x][y];
8082
8083     InitField(newx, newy, FALSE);
8084   }
8085   else if (element == EL_BD_MAGIC_WALL_FILLING)
8086   {
8087     element = Feld[newx][newy] = get_next_element(element);
8088     if (!game.magic_wall_active)
8089       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8090     Store[newx][newy] = Store[x][y];
8091   }
8092   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8093   {
8094     Feld[x][y] = get_next_element(element);
8095     if (!game.magic_wall_active)
8096       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8097     element = Feld[newx][newy] = Store[x][y];
8098
8099     InitField(newx, newy, FALSE);
8100   }
8101   else if (element == EL_DC_MAGIC_WALL_FILLING)
8102   {
8103     element = Feld[newx][newy] = get_next_element(element);
8104     if (!game.magic_wall_active)
8105       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8106     Store[newx][newy] = Store[x][y];
8107   }
8108   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8109   {
8110     Feld[x][y] = get_next_element(element);
8111     if (!game.magic_wall_active)
8112       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8113     element = Feld[newx][newy] = Store[x][y];
8114
8115     InitField(newx, newy, FALSE);
8116   }
8117   else if (element == EL_AMOEBA_DROPPING)
8118   {
8119     Feld[x][y] = get_next_element(element);
8120     element = Feld[newx][newy] = Store[x][y];
8121   }
8122   else if (element == EL_SOKOBAN_OBJECT)
8123   {
8124     if (Back[x][y])
8125       Feld[x][y] = Back[x][y];
8126
8127     if (Back[newx][newy])
8128       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8129
8130     Back[x][y] = Back[newx][newy] = 0;
8131   }
8132
8133   Store[x][y] = EL_EMPTY;
8134   MovPos[x][y] = 0;
8135   MovDir[x][y] = 0;
8136   MovDelay[x][y] = 0;
8137
8138   MovDelay[newx][newy] = 0;
8139
8140   if (CAN_CHANGE_OR_HAS_ACTION(element))
8141   {
8142     /* copy element change control values to new field */
8143     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8144     ChangePage[newx][newy]  = ChangePage[x][y];
8145     ChangeCount[newx][newy] = ChangeCount[x][y];
8146     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8147   }
8148
8149   CustomValue[newx][newy] = CustomValue[x][y];
8150
8151   ChangeDelay[x][y] = 0;
8152   ChangePage[x][y] = -1;
8153   ChangeCount[x][y] = 0;
8154   ChangeEvent[x][y] = -1;
8155
8156   CustomValue[x][y] = 0;
8157
8158   /* copy animation control values to new field */
8159   GfxFrame[newx][newy]  = GfxFrame[x][y];
8160   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8161   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8162   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8163
8164   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8165
8166   /* some elements can leave other elements behind after moving */
8167   if (ei->move_leave_element != EL_EMPTY &&
8168       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8169       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8170   {
8171     int move_leave_element = ei->move_leave_element;
8172
8173     /* this makes it possible to leave the removed element again */
8174     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8175       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8176
8177     Feld[x][y] = move_leave_element;
8178
8179     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8180       MovDir[x][y] = direction;
8181
8182     InitField(x, y, FALSE);
8183
8184     if (GFX_CRUMBLED(Feld[x][y]))
8185       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8186
8187     if (ELEM_IS_PLAYER(move_leave_element))
8188       RelocatePlayer(x, y, move_leave_element);
8189   }
8190
8191   /* do this after checking for left-behind element */
8192   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8193
8194   if (!CAN_MOVE(element) ||
8195       (CAN_FALL(element) && direction == MV_DOWN &&
8196        (element == EL_SPRING ||
8197         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8198         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8199     GfxDir[x][y] = MovDir[newx][newy] = 0;
8200
8201   TEST_DrawLevelField(x, y);
8202   TEST_DrawLevelField(newx, newy);
8203
8204   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8205
8206   /* prevent pushed element from moving on in pushed direction */
8207   if (pushed_by_player && CAN_MOVE(element) &&
8208       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8209       !(element_info[element].move_pattern & direction))
8210     TurnRound(newx, newy);
8211
8212   /* prevent elements on conveyor belt from moving on in last direction */
8213   if (pushed_by_conveyor && CAN_FALL(element) &&
8214       direction & MV_HORIZONTAL)
8215     MovDir[newx][newy] = 0;
8216
8217   if (!pushed_by_player)
8218   {
8219     int nextx = newx + dx, nexty = newy + dy;
8220     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8221
8222     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8223
8224     if (CAN_FALL(element) && direction == MV_DOWN)
8225       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8226
8227     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8228       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8229
8230     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8231       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8232   }
8233
8234   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8235   {
8236     TestIfBadThingTouchesPlayer(newx, newy);
8237     TestIfBadThingTouchesFriend(newx, newy);
8238
8239     if (!IS_CUSTOM_ELEMENT(element))
8240       TestIfBadThingTouchesOtherBadThing(newx, newy);
8241   }
8242   else if (element == EL_PENGUIN)
8243     TestIfFriendTouchesBadThing(newx, newy);
8244
8245   if (DONT_GET_HIT_BY(element))
8246   {
8247     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8248   }
8249
8250   /* give the player one last chance (one more frame) to move away */
8251   if (CAN_FALL(element) && direction == MV_DOWN &&
8252       (last_line || (!IS_FREE(x, newy + 1) &&
8253                      (!IS_PLAYER(x, newy + 1) ||
8254                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8255     Impact(x, newy);
8256
8257   if (pushed_by_player && !game.use_change_when_pushing_bug)
8258   {
8259     int push_side = MV_DIR_OPPOSITE(direction);
8260     struct PlayerInfo *player = PLAYERINFO(x, y);
8261
8262     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8263                                player->index_bit, push_side);
8264     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8265                                         player->index_bit, push_side);
8266   }
8267
8268   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8269     MovDelay[newx][newy] = 1;
8270
8271   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8272
8273   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8274   TestIfElementHitsCustomElement(newx, newy, direction);
8275   TestIfPlayerTouchesCustomElement(newx, newy);
8276   TestIfElementTouchesCustomElement(newx, newy);
8277
8278   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8279       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8280     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8281                              MV_DIR_OPPOSITE(direction));
8282 }
8283
8284 int AmoebeNachbarNr(int ax, int ay)
8285 {
8286   int i;
8287   int element = Feld[ax][ay];
8288   int group_nr = 0;
8289   static int xy[4][2] =
8290   {
8291     { 0, -1 },
8292     { -1, 0 },
8293     { +1, 0 },
8294     { 0, +1 }
8295   };
8296
8297   for (i = 0; i < NUM_DIRECTIONS; i++)
8298   {
8299     int x = ax + xy[i][0];
8300     int y = ay + xy[i][1];
8301
8302     if (!IN_LEV_FIELD(x, y))
8303       continue;
8304
8305     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8306       group_nr = AmoebaNr[x][y];
8307   }
8308
8309   return group_nr;
8310 }
8311
8312 void AmoebenVereinigen(int ax, int ay)
8313 {
8314   int i, x, y, xx, yy;
8315   int new_group_nr = AmoebaNr[ax][ay];
8316   static int xy[4][2] =
8317   {
8318     { 0, -1 },
8319     { -1, 0 },
8320     { +1, 0 },
8321     { 0, +1 }
8322   };
8323
8324   if (new_group_nr == 0)
8325     return;
8326
8327   for (i = 0; i < NUM_DIRECTIONS; i++)
8328   {
8329     x = ax + xy[i][0];
8330     y = ay + xy[i][1];
8331
8332     if (!IN_LEV_FIELD(x, y))
8333       continue;
8334
8335     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8336          Feld[x][y] == EL_BD_AMOEBA ||
8337          Feld[x][y] == EL_AMOEBA_DEAD) &&
8338         AmoebaNr[x][y] != new_group_nr)
8339     {
8340       int old_group_nr = AmoebaNr[x][y];
8341
8342       if (old_group_nr == 0)
8343         return;
8344
8345       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8346       AmoebaCnt[old_group_nr] = 0;
8347       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8348       AmoebaCnt2[old_group_nr] = 0;
8349
8350       SCAN_PLAYFIELD(xx, yy)
8351       {
8352         if (AmoebaNr[xx][yy] == old_group_nr)
8353           AmoebaNr[xx][yy] = new_group_nr;
8354       }
8355     }
8356   }
8357 }
8358
8359 void AmoebeUmwandeln(int ax, int ay)
8360 {
8361   int i, x, y;
8362
8363   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8364   {
8365     int group_nr = AmoebaNr[ax][ay];
8366
8367 #ifdef DEBUG
8368     if (group_nr == 0)
8369     {
8370       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8371       printf("AmoebeUmwandeln(): This should never happen!\n");
8372       return;
8373     }
8374 #endif
8375
8376     SCAN_PLAYFIELD(x, y)
8377     {
8378       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8379       {
8380         AmoebaNr[x][y] = 0;
8381         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8382       }
8383     }
8384
8385     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8386                             SND_AMOEBA_TURNING_TO_GEM :
8387                             SND_AMOEBA_TURNING_TO_ROCK));
8388     Bang(ax, ay);
8389   }
8390   else
8391   {
8392     static int xy[4][2] =
8393     {
8394       { 0, -1 },
8395       { -1, 0 },
8396       { +1, 0 },
8397       { 0, +1 }
8398     };
8399
8400     for (i = 0; i < NUM_DIRECTIONS; i++)
8401     {
8402       x = ax + xy[i][0];
8403       y = ay + xy[i][1];
8404
8405       if (!IN_LEV_FIELD(x, y))
8406         continue;
8407
8408       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8409       {
8410         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8411                               SND_AMOEBA_TURNING_TO_GEM :
8412                               SND_AMOEBA_TURNING_TO_ROCK));
8413         Bang(x, y);
8414       }
8415     }
8416   }
8417 }
8418
8419 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8420 {
8421   int x, y;
8422   int group_nr = AmoebaNr[ax][ay];
8423   boolean done = FALSE;
8424
8425 #ifdef DEBUG
8426   if (group_nr == 0)
8427   {
8428     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8429     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8430     return;
8431   }
8432 #endif
8433
8434   SCAN_PLAYFIELD(x, y)
8435   {
8436     if (AmoebaNr[x][y] == group_nr &&
8437         (Feld[x][y] == EL_AMOEBA_DEAD ||
8438          Feld[x][y] == EL_BD_AMOEBA ||
8439          Feld[x][y] == EL_AMOEBA_GROWING))
8440     {
8441       AmoebaNr[x][y] = 0;
8442       Feld[x][y] = new_element;
8443       InitField(x, y, FALSE);
8444       TEST_DrawLevelField(x, y);
8445       done = TRUE;
8446     }
8447   }
8448
8449   if (done)
8450     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8451                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8452                             SND_BD_AMOEBA_TURNING_TO_GEM));
8453 }
8454
8455 void AmoebeWaechst(int x, int y)
8456 {
8457   static unsigned int sound_delay = 0;
8458   static unsigned int sound_delay_value = 0;
8459
8460   if (!MovDelay[x][y])          /* start new growing cycle */
8461   {
8462     MovDelay[x][y] = 7;
8463
8464     if (DelayReached(&sound_delay, sound_delay_value))
8465     {
8466       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8467       sound_delay_value = 30;
8468     }
8469   }
8470
8471   if (MovDelay[x][y])           /* wait some time before growing bigger */
8472   {
8473     MovDelay[x][y]--;
8474     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8475     {
8476       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8477                                            6 - MovDelay[x][y]);
8478
8479       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8480     }
8481
8482     if (!MovDelay[x][y])
8483     {
8484       Feld[x][y] = Store[x][y];
8485       Store[x][y] = 0;
8486       TEST_DrawLevelField(x, y);
8487     }
8488   }
8489 }
8490
8491 void AmoebaDisappearing(int x, int y)
8492 {
8493   static unsigned int sound_delay = 0;
8494   static unsigned int sound_delay_value = 0;
8495
8496   if (!MovDelay[x][y])          /* start new shrinking cycle */
8497   {
8498     MovDelay[x][y] = 7;
8499
8500     if (DelayReached(&sound_delay, sound_delay_value))
8501       sound_delay_value = 30;
8502   }
8503
8504   if (MovDelay[x][y])           /* wait some time before shrinking */
8505   {
8506     MovDelay[x][y]--;
8507     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8508     {
8509       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8510                                            6 - MovDelay[x][y]);
8511
8512       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8513     }
8514
8515     if (!MovDelay[x][y])
8516     {
8517       Feld[x][y] = EL_EMPTY;
8518       TEST_DrawLevelField(x, y);
8519
8520       /* don't let mole enter this field in this cycle;
8521          (give priority to objects falling to this field from above) */
8522       Stop[x][y] = TRUE;
8523     }
8524   }
8525 }
8526
8527 void AmoebeAbleger(int ax, int ay)
8528 {
8529   int i;
8530   int element = Feld[ax][ay];
8531   int graphic = el2img(element);
8532   int newax = ax, neway = ay;
8533   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8534   static int xy[4][2] =
8535   {
8536     { 0, -1 },
8537     { -1, 0 },
8538     { +1, 0 },
8539     { 0, +1 }
8540   };
8541
8542   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8543   {
8544     Feld[ax][ay] = EL_AMOEBA_DEAD;
8545     TEST_DrawLevelField(ax, ay);
8546     return;
8547   }
8548
8549   if (IS_ANIMATED(graphic))
8550     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8551
8552   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8553     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8554
8555   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8556   {
8557     MovDelay[ax][ay]--;
8558     if (MovDelay[ax][ay])
8559       return;
8560   }
8561
8562   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8563   {
8564     int start = RND(4);
8565     int x = ax + xy[start][0];
8566     int y = ay + xy[start][1];
8567
8568     if (!IN_LEV_FIELD(x, y))
8569       return;
8570
8571     if (IS_FREE(x, y) ||
8572         CAN_GROW_INTO(Feld[x][y]) ||
8573         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8574         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8575     {
8576       newax = x;
8577       neway = y;
8578     }
8579
8580     if (newax == ax && neway == ay)
8581       return;
8582   }
8583   else                          /* normal or "filled" (BD style) amoeba */
8584   {
8585     int start = RND(4);
8586     boolean waiting_for_player = FALSE;
8587
8588     for (i = 0; i < NUM_DIRECTIONS; i++)
8589     {
8590       int j = (start + i) % 4;
8591       int x = ax + xy[j][0];
8592       int y = ay + xy[j][1];
8593
8594       if (!IN_LEV_FIELD(x, y))
8595         continue;
8596
8597       if (IS_FREE(x, y) ||
8598           CAN_GROW_INTO(Feld[x][y]) ||
8599           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8600           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8601       {
8602         newax = x;
8603         neway = y;
8604         break;
8605       }
8606       else if (IS_PLAYER(x, y))
8607         waiting_for_player = TRUE;
8608     }
8609
8610     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8611     {
8612       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8613       {
8614         Feld[ax][ay] = EL_AMOEBA_DEAD;
8615         TEST_DrawLevelField(ax, ay);
8616         AmoebaCnt[AmoebaNr[ax][ay]]--;
8617
8618         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8619         {
8620           if (element == EL_AMOEBA_FULL)
8621             AmoebeUmwandeln(ax, ay);
8622           else if (element == EL_BD_AMOEBA)
8623             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8624         }
8625       }
8626       return;
8627     }
8628     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8629     {
8630       /* amoeba gets larger by growing in some direction */
8631
8632       int new_group_nr = AmoebaNr[ax][ay];
8633
8634 #ifdef DEBUG
8635   if (new_group_nr == 0)
8636   {
8637     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8638     printf("AmoebeAbleger(): This should never happen!\n");
8639     return;
8640   }
8641 #endif
8642
8643       AmoebaNr[newax][neway] = new_group_nr;
8644       AmoebaCnt[new_group_nr]++;
8645       AmoebaCnt2[new_group_nr]++;
8646
8647       /* if amoeba touches other amoeba(s) after growing, unify them */
8648       AmoebenVereinigen(newax, neway);
8649
8650       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8651       {
8652         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8653         return;
8654       }
8655     }
8656   }
8657
8658   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8659       (neway == lev_fieldy - 1 && newax != ax))
8660   {
8661     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8662     Store[newax][neway] = element;
8663   }
8664   else if (neway == ay || element == EL_EMC_DRIPPER)
8665   {
8666     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8667
8668     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8669   }
8670   else
8671   {
8672     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8673     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8674     Store[ax][ay] = EL_AMOEBA_DROP;
8675     ContinueMoving(ax, ay);
8676     return;
8677   }
8678
8679   TEST_DrawLevelField(newax, neway);
8680 }
8681
8682 void Life(int ax, int ay)
8683 {
8684   int x1, y1, x2, y2;
8685   int life_time = 40;
8686   int element = Feld[ax][ay];
8687   int graphic = el2img(element);
8688   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8689                          level.biomaze);
8690   boolean changed = FALSE;
8691
8692   if (IS_ANIMATED(graphic))
8693     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8694
8695   if (Stop[ax][ay])
8696     return;
8697
8698   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8699     MovDelay[ax][ay] = life_time;
8700
8701   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8702   {
8703     MovDelay[ax][ay]--;
8704     if (MovDelay[ax][ay])
8705       return;
8706   }
8707
8708   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8709   {
8710     int xx = ax+x1, yy = ay+y1;
8711     int nachbarn = 0;
8712
8713     if (!IN_LEV_FIELD(xx, yy))
8714       continue;
8715
8716     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8717     {
8718       int x = xx+x2, y = yy+y2;
8719
8720       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8721         continue;
8722
8723       if (((Feld[x][y] == element ||
8724             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8725            !Stop[x][y]) ||
8726           (IS_FREE(x, y) && Stop[x][y]))
8727         nachbarn++;
8728     }
8729
8730     if (xx == ax && yy == ay)           /* field in the middle */
8731     {
8732       if (nachbarn < life_parameter[0] ||
8733           nachbarn > life_parameter[1])
8734       {
8735         Feld[xx][yy] = EL_EMPTY;
8736         if (!Stop[xx][yy])
8737           TEST_DrawLevelField(xx, yy);
8738         Stop[xx][yy] = TRUE;
8739         changed = TRUE;
8740       }
8741     }
8742     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8743     {                                   /* free border field */
8744       if (nachbarn >= life_parameter[2] &&
8745           nachbarn <= life_parameter[3])
8746       {
8747         Feld[xx][yy] = element;
8748         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8749         if (!Stop[xx][yy])
8750           TEST_DrawLevelField(xx, yy);
8751         Stop[xx][yy] = TRUE;
8752         changed = TRUE;
8753       }
8754     }
8755   }
8756
8757   if (changed)
8758     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8759                    SND_GAME_OF_LIFE_GROWING);
8760 }
8761
8762 static void InitRobotWheel(int x, int y)
8763 {
8764   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8765 }
8766
8767 static void RunRobotWheel(int x, int y)
8768 {
8769   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8770 }
8771
8772 static void StopRobotWheel(int x, int y)
8773 {
8774   if (ZX == x && ZY == y)
8775   {
8776     ZX = ZY = -1;
8777
8778     game.robot_wheel_active = FALSE;
8779   }
8780 }
8781
8782 static void InitTimegateWheel(int x, int y)
8783 {
8784   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8785 }
8786
8787 static void RunTimegateWheel(int x, int y)
8788 {
8789   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8790 }
8791
8792 static void InitMagicBallDelay(int x, int y)
8793 {
8794   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8795 }
8796
8797 static void ActivateMagicBall(int bx, int by)
8798 {
8799   int x, y;
8800
8801   if (level.ball_random)
8802   {
8803     int pos_border = RND(8);    /* select one of the eight border elements */
8804     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8805     int xx = pos_content % 3;
8806     int yy = pos_content / 3;
8807
8808     x = bx - 1 + xx;
8809     y = by - 1 + yy;
8810
8811     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8812       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8813   }
8814   else
8815   {
8816     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8817     {
8818       int xx = x - bx + 1;
8819       int yy = y - by + 1;
8820
8821       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8822         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8823     }
8824   }
8825
8826   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8827 }
8828
8829 void CheckExit(int x, int y)
8830 {
8831   if (local_player->gems_still_needed > 0 ||
8832       local_player->sokobanfields_still_needed > 0 ||
8833       local_player->lights_still_needed > 0)
8834   {
8835     int element = Feld[x][y];
8836     int graphic = el2img(element);
8837
8838     if (IS_ANIMATED(graphic))
8839       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8840
8841     return;
8842   }
8843
8844   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8845     return;
8846
8847   Feld[x][y] = EL_EXIT_OPENING;
8848
8849   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8850 }
8851
8852 void CheckExitEM(int x, int y)
8853 {
8854   if (local_player->gems_still_needed > 0 ||
8855       local_player->sokobanfields_still_needed > 0 ||
8856       local_player->lights_still_needed > 0)
8857   {
8858     int element = Feld[x][y];
8859     int graphic = el2img(element);
8860
8861     if (IS_ANIMATED(graphic))
8862       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8863
8864     return;
8865   }
8866
8867   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8868     return;
8869
8870   Feld[x][y] = EL_EM_EXIT_OPENING;
8871
8872   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8873 }
8874
8875 void CheckExitSteel(int x, int y)
8876 {
8877   if (local_player->gems_still_needed > 0 ||
8878       local_player->sokobanfields_still_needed > 0 ||
8879       local_player->lights_still_needed > 0)
8880   {
8881     int element = Feld[x][y];
8882     int graphic = el2img(element);
8883
8884     if (IS_ANIMATED(graphic))
8885       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8886
8887     return;
8888   }
8889
8890   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8891     return;
8892
8893   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8894
8895   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8896 }
8897
8898 void CheckExitSteelEM(int x, int y)
8899 {
8900   if (local_player->gems_still_needed > 0 ||
8901       local_player->sokobanfields_still_needed > 0 ||
8902       local_player->lights_still_needed > 0)
8903   {
8904     int element = Feld[x][y];
8905     int graphic = el2img(element);
8906
8907     if (IS_ANIMATED(graphic))
8908       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8909
8910     return;
8911   }
8912
8913   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8914     return;
8915
8916   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8917
8918   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8919 }
8920
8921 void CheckExitSP(int x, int y)
8922 {
8923   if (local_player->gems_still_needed > 0)
8924   {
8925     int element = Feld[x][y];
8926     int graphic = el2img(element);
8927
8928     if (IS_ANIMATED(graphic))
8929       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8930
8931     return;
8932   }
8933
8934   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8935     return;
8936
8937   Feld[x][y] = EL_SP_EXIT_OPENING;
8938
8939   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8940 }
8941
8942 static void CloseAllOpenTimegates()
8943 {
8944   int x, y;
8945
8946   SCAN_PLAYFIELD(x, y)
8947   {
8948     int element = Feld[x][y];
8949
8950     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8951     {
8952       Feld[x][y] = EL_TIMEGATE_CLOSING;
8953
8954       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8955     }
8956   }
8957 }
8958
8959 void DrawTwinkleOnField(int x, int y)
8960 {
8961   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8962     return;
8963
8964   if (Feld[x][y] == EL_BD_DIAMOND)
8965     return;
8966
8967   if (MovDelay[x][y] == 0)      /* next animation frame */
8968     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8969
8970   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8971   {
8972     MovDelay[x][y]--;
8973
8974     DrawLevelElementAnimation(x, y, Feld[x][y]);
8975
8976     if (MovDelay[x][y] != 0)
8977     {
8978       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8979                                            10 - MovDelay[x][y]);
8980
8981       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8982     }
8983   }
8984 }
8985
8986 void MauerWaechst(int x, int y)
8987 {
8988   int delay = 6;
8989
8990   if (!MovDelay[x][y])          /* next animation frame */
8991     MovDelay[x][y] = 3 * delay;
8992
8993   if (MovDelay[x][y])           /* wait some time before next frame */
8994   {
8995     MovDelay[x][y]--;
8996
8997     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8998     {
8999       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9000       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9001
9002       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9003     }
9004
9005     if (!MovDelay[x][y])
9006     {
9007       if (MovDir[x][y] == MV_LEFT)
9008       {
9009         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9010           TEST_DrawLevelField(x - 1, y);
9011       }
9012       else if (MovDir[x][y] == MV_RIGHT)
9013       {
9014         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9015           TEST_DrawLevelField(x + 1, y);
9016       }
9017       else if (MovDir[x][y] == MV_UP)
9018       {
9019         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9020           TEST_DrawLevelField(x, y - 1);
9021       }
9022       else
9023       {
9024         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9025           TEST_DrawLevelField(x, y + 1);
9026       }
9027
9028       Feld[x][y] = Store[x][y];
9029       Store[x][y] = 0;
9030       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9031       TEST_DrawLevelField(x, y);
9032     }
9033   }
9034 }
9035
9036 void MauerAbleger(int ax, int ay)
9037 {
9038   int element = Feld[ax][ay];
9039   int graphic = el2img(element);
9040   boolean oben_frei = FALSE, unten_frei = FALSE;
9041   boolean links_frei = FALSE, rechts_frei = FALSE;
9042   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9043   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9044   boolean new_wall = FALSE;
9045
9046   if (IS_ANIMATED(graphic))
9047     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9048
9049   if (!MovDelay[ax][ay])        /* start building new wall */
9050     MovDelay[ax][ay] = 6;
9051
9052   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9053   {
9054     MovDelay[ax][ay]--;
9055     if (MovDelay[ax][ay])
9056       return;
9057   }
9058
9059   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9060     oben_frei = TRUE;
9061   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9062     unten_frei = TRUE;
9063   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9064     links_frei = TRUE;
9065   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9066     rechts_frei = TRUE;
9067
9068   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9069       element == EL_EXPANDABLE_WALL_ANY)
9070   {
9071     if (oben_frei)
9072     {
9073       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9074       Store[ax][ay-1] = element;
9075       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9076       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9077         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9078                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9079       new_wall = TRUE;
9080     }
9081     if (unten_frei)
9082     {
9083       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9084       Store[ax][ay+1] = element;
9085       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9086       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9087         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9088                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9089       new_wall = TRUE;
9090     }
9091   }
9092
9093   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9094       element == EL_EXPANDABLE_WALL_ANY ||
9095       element == EL_EXPANDABLE_WALL ||
9096       element == EL_BD_EXPANDABLE_WALL)
9097   {
9098     if (links_frei)
9099     {
9100       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9101       Store[ax-1][ay] = element;
9102       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9103       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9104         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9105                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9106       new_wall = TRUE;
9107     }
9108
9109     if (rechts_frei)
9110     {
9111       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9112       Store[ax+1][ay] = element;
9113       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9114       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9115         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9116                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9117       new_wall = TRUE;
9118     }
9119   }
9120
9121   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9122     TEST_DrawLevelField(ax, ay);
9123
9124   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9125     oben_massiv = TRUE;
9126   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9127     unten_massiv = TRUE;
9128   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9129     links_massiv = TRUE;
9130   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9131     rechts_massiv = TRUE;
9132
9133   if (((oben_massiv && unten_massiv) ||
9134        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9135        element == EL_EXPANDABLE_WALL) &&
9136       ((links_massiv && rechts_massiv) ||
9137        element == EL_EXPANDABLE_WALL_VERTICAL))
9138     Feld[ax][ay] = EL_WALL;
9139
9140   if (new_wall)
9141     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9142 }
9143
9144 void MauerAblegerStahl(int ax, int ay)
9145 {
9146   int element = Feld[ax][ay];
9147   int graphic = el2img(element);
9148   boolean oben_frei = FALSE, unten_frei = FALSE;
9149   boolean links_frei = FALSE, rechts_frei = FALSE;
9150   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9151   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9152   boolean new_wall = FALSE;
9153
9154   if (IS_ANIMATED(graphic))
9155     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9156
9157   if (!MovDelay[ax][ay])        /* start building new wall */
9158     MovDelay[ax][ay] = 6;
9159
9160   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9161   {
9162     MovDelay[ax][ay]--;
9163     if (MovDelay[ax][ay])
9164       return;
9165   }
9166
9167   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9168     oben_frei = TRUE;
9169   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9170     unten_frei = TRUE;
9171   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9172     links_frei = TRUE;
9173   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9174     rechts_frei = TRUE;
9175
9176   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9177       element == EL_EXPANDABLE_STEELWALL_ANY)
9178   {
9179     if (oben_frei)
9180     {
9181       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9182       Store[ax][ay-1] = element;
9183       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9184       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9185         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9186                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9187       new_wall = TRUE;
9188     }
9189     if (unten_frei)
9190     {
9191       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9192       Store[ax][ay+1] = element;
9193       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9194       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9195         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9196                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9197       new_wall = TRUE;
9198     }
9199   }
9200
9201   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9202       element == EL_EXPANDABLE_STEELWALL_ANY)
9203   {
9204     if (links_frei)
9205     {
9206       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9207       Store[ax-1][ay] = element;
9208       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9209       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9210         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9211                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9212       new_wall = TRUE;
9213     }
9214
9215     if (rechts_frei)
9216     {
9217       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9218       Store[ax+1][ay] = element;
9219       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9220       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9221         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9222                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9223       new_wall = TRUE;
9224     }
9225   }
9226
9227   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9228     oben_massiv = TRUE;
9229   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9230     unten_massiv = TRUE;
9231   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9232     links_massiv = TRUE;
9233   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9234     rechts_massiv = TRUE;
9235
9236   if (((oben_massiv && unten_massiv) ||
9237        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9238       ((links_massiv && rechts_massiv) ||
9239        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9240     Feld[ax][ay] = EL_STEELWALL;
9241
9242   if (new_wall)
9243     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9244 }
9245
9246 void CheckForDragon(int x, int y)
9247 {
9248   int i, j;
9249   boolean dragon_found = FALSE;
9250   static int xy[4][2] =
9251   {
9252     { 0, -1 },
9253     { -1, 0 },
9254     { +1, 0 },
9255     { 0, +1 }
9256   };
9257
9258   for (i = 0; i < NUM_DIRECTIONS; i++)
9259   {
9260     for (j = 0; j < 4; j++)
9261     {
9262       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9263
9264       if (IN_LEV_FIELD(xx, yy) &&
9265           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9266       {
9267         if (Feld[xx][yy] == EL_DRAGON)
9268           dragon_found = TRUE;
9269       }
9270       else
9271         break;
9272     }
9273   }
9274
9275   if (!dragon_found)
9276   {
9277     for (i = 0; i < NUM_DIRECTIONS; i++)
9278     {
9279       for (j = 0; j < 3; j++)
9280       {
9281         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9282   
9283         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9284         {
9285           Feld[xx][yy] = EL_EMPTY;
9286           TEST_DrawLevelField(xx, yy);
9287         }
9288         else
9289           break;
9290       }
9291     }
9292   }
9293 }
9294
9295 static void InitBuggyBase(int x, int y)
9296 {
9297   int element = Feld[x][y];
9298   int activating_delay = FRAMES_PER_SECOND / 4;
9299
9300   ChangeDelay[x][y] =
9301     (element == EL_SP_BUGGY_BASE ?
9302      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9303      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9304      activating_delay :
9305      element == EL_SP_BUGGY_BASE_ACTIVE ?
9306      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9307 }
9308
9309 static void WarnBuggyBase(int x, int y)
9310 {
9311   int i;
9312   static int xy[4][2] =
9313   {
9314     { 0, -1 },
9315     { -1, 0 },
9316     { +1, 0 },
9317     { 0, +1 }
9318   };
9319
9320   for (i = 0; i < NUM_DIRECTIONS; i++)
9321   {
9322     int xx = x + xy[i][0];
9323     int yy = y + xy[i][1];
9324
9325     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9326     {
9327       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9328
9329       break;
9330     }
9331   }
9332 }
9333
9334 static void InitTrap(int x, int y)
9335 {
9336   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9337 }
9338
9339 static void ActivateTrap(int x, int y)
9340 {
9341   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9342 }
9343
9344 static void ChangeActiveTrap(int x, int y)
9345 {
9346   int graphic = IMG_TRAP_ACTIVE;
9347
9348   /* if new animation frame was drawn, correct crumbled sand border */
9349   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9350     TEST_DrawLevelFieldCrumbled(x, y);
9351 }
9352
9353 static int getSpecialActionElement(int element, int number, int base_element)
9354 {
9355   return (element != EL_EMPTY ? element :
9356           number != -1 ? base_element + number - 1 :
9357           EL_EMPTY);
9358 }
9359
9360 static int getModifiedActionNumber(int value_old, int operator, int operand,
9361                                    int value_min, int value_max)
9362 {
9363   int value_new = (operator == CA_MODE_SET      ? operand :
9364                    operator == CA_MODE_ADD      ? value_old + operand :
9365                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9366                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9367                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9368                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9369                    value_old);
9370
9371   return (value_new < value_min ? value_min :
9372           value_new > value_max ? value_max :
9373           value_new);
9374 }
9375
9376 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9377 {
9378   struct ElementInfo *ei = &element_info[element];
9379   struct ElementChangeInfo *change = &ei->change_page[page];
9380   int target_element = change->target_element;
9381   int action_type = change->action_type;
9382   int action_mode = change->action_mode;
9383   int action_arg = change->action_arg;
9384   int action_element = change->action_element;
9385   int i;
9386
9387   if (!change->has_action)
9388     return;
9389
9390   /* ---------- determine action paramater values -------------------------- */
9391
9392   int level_time_value =
9393     (level.time > 0 ? TimeLeft :
9394      TimePlayed);
9395
9396   int action_arg_element_raw =
9397     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9398      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9399      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9400      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9401      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9402      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9403      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9404      EL_EMPTY);
9405   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9406
9407   int action_arg_direction =
9408     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9409      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9410      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9411      change->actual_trigger_side :
9412      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9413      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9414      MV_NONE);
9415
9416   int action_arg_number_min =
9417     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9418      CA_ARG_MIN);
9419
9420   int action_arg_number_max =
9421     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9422      action_type == CA_SET_LEVEL_GEMS ? 999 :
9423      action_type == CA_SET_LEVEL_TIME ? 9999 :
9424      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9425      action_type == CA_SET_CE_VALUE ? 9999 :
9426      action_type == CA_SET_CE_SCORE ? 9999 :
9427      CA_ARG_MAX);
9428
9429   int action_arg_number_reset =
9430     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9431      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9432      action_type == CA_SET_LEVEL_TIME ? level.time :
9433      action_type == CA_SET_LEVEL_SCORE ? 0 :
9434      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9435      action_type == CA_SET_CE_SCORE ? 0 :
9436      0);
9437
9438   int action_arg_number =
9439     (action_arg <= CA_ARG_MAX ? action_arg :
9440      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9441      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9442      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9443      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9444      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9445      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9446      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9447      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9448      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9449      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9450      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9451      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9452      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9453      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9454      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9455      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9456      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9457      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9458      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9459      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9460      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9461      -1);
9462
9463   int action_arg_number_old =
9464     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9465      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9466      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9467      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9468      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9469      0);
9470
9471   int action_arg_number_new =
9472     getModifiedActionNumber(action_arg_number_old,
9473                             action_mode, action_arg_number,
9474                             action_arg_number_min, action_arg_number_max);
9475
9476   int trigger_player_bits =
9477     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9478      change->actual_trigger_player_bits : change->trigger_player);
9479
9480   int action_arg_player_bits =
9481     (action_arg >= CA_ARG_PLAYER_1 &&
9482      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9483      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9484      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9485      PLAYER_BITS_ANY);
9486
9487   /* ---------- execute action  -------------------------------------------- */
9488
9489   switch (action_type)
9490   {
9491     case CA_NO_ACTION:
9492     {
9493       return;
9494     }
9495
9496     /* ---------- level actions  ------------------------------------------- */
9497
9498     case CA_RESTART_LEVEL:
9499     {
9500       game.restart_level = TRUE;
9501
9502       break;
9503     }
9504
9505     case CA_SHOW_ENVELOPE:
9506     {
9507       int element = getSpecialActionElement(action_arg_element,
9508                                             action_arg_number, EL_ENVELOPE_1);
9509
9510       if (IS_ENVELOPE(element))
9511         local_player->show_envelope = element;
9512
9513       break;
9514     }
9515
9516     case CA_SET_LEVEL_TIME:
9517     {
9518       if (level.time > 0)       /* only modify limited time value */
9519       {
9520         TimeLeft = action_arg_number_new;
9521
9522         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9523
9524         DisplayGameControlValues();
9525
9526         if (!TimeLeft && setup.time_limit)
9527           for (i = 0; i < MAX_PLAYERS; i++)
9528             KillPlayer(&stored_player[i]);
9529       }
9530
9531       break;
9532     }
9533
9534     case CA_SET_LEVEL_SCORE:
9535     {
9536       local_player->score = action_arg_number_new;
9537
9538       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9539
9540       DisplayGameControlValues();
9541
9542       break;
9543     }
9544
9545     case CA_SET_LEVEL_GEMS:
9546     {
9547       local_player->gems_still_needed = action_arg_number_new;
9548
9549       game_panel_controls[GAME_PANEL_GEMS].value =
9550         local_player->gems_still_needed;
9551
9552       DisplayGameControlValues();
9553
9554       break;
9555     }
9556
9557     case CA_SET_LEVEL_WIND:
9558     {
9559       game.wind_direction = action_arg_direction;
9560
9561       break;
9562     }
9563
9564     case CA_SET_LEVEL_RANDOM_SEED:
9565     {
9566       /* ensure that setting a new random seed while playing is predictable */
9567       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9568
9569       break;
9570     }
9571
9572     /* ---------- player actions  ------------------------------------------ */
9573
9574     case CA_MOVE_PLAYER:
9575     {
9576       /* automatically move to the next field in specified direction */
9577       for (i = 0; i < MAX_PLAYERS; i++)
9578         if (trigger_player_bits & (1 << i))
9579           stored_player[i].programmed_action = action_arg_direction;
9580
9581       break;
9582     }
9583
9584     case CA_EXIT_PLAYER:
9585     {
9586       for (i = 0; i < MAX_PLAYERS; i++)
9587         if (action_arg_player_bits & (1 << i))
9588           PlayerWins(&stored_player[i]);
9589
9590       break;
9591     }
9592
9593     case CA_KILL_PLAYER:
9594     {
9595       for (i = 0; i < MAX_PLAYERS; i++)
9596         if (action_arg_player_bits & (1 << i))
9597           KillPlayer(&stored_player[i]);
9598
9599       break;
9600     }
9601
9602     case CA_SET_PLAYER_KEYS:
9603     {
9604       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9605       int element = getSpecialActionElement(action_arg_element,
9606                                             action_arg_number, EL_KEY_1);
9607
9608       if (IS_KEY(element))
9609       {
9610         for (i = 0; i < MAX_PLAYERS; i++)
9611         {
9612           if (trigger_player_bits & (1 << i))
9613           {
9614             stored_player[i].key[KEY_NR(element)] = key_state;
9615
9616             DrawGameDoorValues();
9617           }
9618         }
9619       }
9620
9621       break;
9622     }
9623
9624     case CA_SET_PLAYER_SPEED:
9625     {
9626       for (i = 0; i < MAX_PLAYERS; i++)
9627       {
9628         if (trigger_player_bits & (1 << i))
9629         {
9630           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9631
9632           if (action_arg == CA_ARG_SPEED_FASTER &&
9633               stored_player[i].cannot_move)
9634           {
9635             action_arg_number = STEPSIZE_VERY_SLOW;
9636           }
9637           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9638                    action_arg == CA_ARG_SPEED_FASTER)
9639           {
9640             action_arg_number = 2;
9641             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9642                            CA_MODE_MULTIPLY);
9643           }
9644           else if (action_arg == CA_ARG_NUMBER_RESET)
9645           {
9646             action_arg_number = level.initial_player_stepsize[i];
9647           }
9648
9649           move_stepsize =
9650             getModifiedActionNumber(move_stepsize,
9651                                     action_mode,
9652                                     action_arg_number,
9653                                     action_arg_number_min,
9654                                     action_arg_number_max);
9655
9656           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9657         }
9658       }
9659
9660       break;
9661     }
9662
9663     case CA_SET_PLAYER_SHIELD:
9664     {
9665       for (i = 0; i < MAX_PLAYERS; i++)
9666       {
9667         if (trigger_player_bits & (1 << i))
9668         {
9669           if (action_arg == CA_ARG_SHIELD_OFF)
9670           {
9671             stored_player[i].shield_normal_time_left = 0;
9672             stored_player[i].shield_deadly_time_left = 0;
9673           }
9674           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9675           {
9676             stored_player[i].shield_normal_time_left = 999999;
9677           }
9678           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9679           {
9680             stored_player[i].shield_normal_time_left = 999999;
9681             stored_player[i].shield_deadly_time_left = 999999;
9682           }
9683         }
9684       }
9685
9686       break;
9687     }
9688
9689     case CA_SET_PLAYER_GRAVITY:
9690     {
9691       for (i = 0; i < MAX_PLAYERS; i++)
9692       {
9693         if (trigger_player_bits & (1 << i))
9694         {
9695           stored_player[i].gravity =
9696             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9697              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9698              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9699              stored_player[i].gravity);
9700         }
9701       }
9702
9703       break;
9704     }
9705
9706     case CA_SET_PLAYER_ARTWORK:
9707     {
9708       for (i = 0; i < MAX_PLAYERS; i++)
9709       {
9710         if (trigger_player_bits & (1 << i))
9711         {
9712           int artwork_element = action_arg_element;
9713
9714           if (action_arg == CA_ARG_ELEMENT_RESET)
9715             artwork_element =
9716               (level.use_artwork_element[i] ? level.artwork_element[i] :
9717                stored_player[i].element_nr);
9718
9719           if (stored_player[i].artwork_element != artwork_element)
9720             stored_player[i].Frame = 0;
9721
9722           stored_player[i].artwork_element = artwork_element;
9723
9724           SetPlayerWaiting(&stored_player[i], FALSE);
9725
9726           /* set number of special actions for bored and sleeping animation */
9727           stored_player[i].num_special_action_bored =
9728             get_num_special_action(artwork_element,
9729                                    ACTION_BORING_1, ACTION_BORING_LAST);
9730           stored_player[i].num_special_action_sleeping =
9731             get_num_special_action(artwork_element,
9732                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9733         }
9734       }
9735
9736       break;
9737     }
9738
9739     case CA_SET_PLAYER_INVENTORY:
9740     {
9741       for (i = 0; i < MAX_PLAYERS; i++)
9742       {
9743         struct PlayerInfo *player = &stored_player[i];
9744         int j, k;
9745
9746         if (trigger_player_bits & (1 << i))
9747         {
9748           int inventory_element = action_arg_element;
9749
9750           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9751               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9752               action_arg == CA_ARG_ELEMENT_ACTION)
9753           {
9754             int element = inventory_element;
9755             int collect_count = element_info[element].collect_count_initial;
9756
9757             if (!IS_CUSTOM_ELEMENT(element))
9758               collect_count = 1;
9759
9760             if (collect_count == 0)
9761               player->inventory_infinite_element = element;
9762             else
9763               for (k = 0; k < collect_count; k++)
9764                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9765                   player->inventory_element[player->inventory_size++] =
9766                     element;
9767           }
9768           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9769                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9770                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9771           {
9772             if (player->inventory_infinite_element != EL_UNDEFINED &&
9773                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9774                                      action_arg_element_raw))
9775               player->inventory_infinite_element = EL_UNDEFINED;
9776
9777             for (k = 0, j = 0; j < player->inventory_size; j++)
9778             {
9779               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9780                                         action_arg_element_raw))
9781                 player->inventory_element[k++] = player->inventory_element[j];
9782             }
9783
9784             player->inventory_size = k;
9785           }
9786           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9787           {
9788             if (player->inventory_size > 0)
9789             {
9790               for (j = 0; j < player->inventory_size - 1; j++)
9791                 player->inventory_element[j] = player->inventory_element[j + 1];
9792
9793               player->inventory_size--;
9794             }
9795           }
9796           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9797           {
9798             if (player->inventory_size > 0)
9799               player->inventory_size--;
9800           }
9801           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9802           {
9803             player->inventory_infinite_element = EL_UNDEFINED;
9804             player->inventory_size = 0;
9805           }
9806           else if (action_arg == CA_ARG_INVENTORY_RESET)
9807           {
9808             player->inventory_infinite_element = EL_UNDEFINED;
9809             player->inventory_size = 0;
9810
9811             if (level.use_initial_inventory[i])
9812             {
9813               for (j = 0; j < level.initial_inventory_size[i]; j++)
9814               {
9815                 int element = level.initial_inventory_content[i][j];
9816                 int collect_count = element_info[element].collect_count_initial;
9817
9818                 if (!IS_CUSTOM_ELEMENT(element))
9819                   collect_count = 1;
9820
9821                 if (collect_count == 0)
9822                   player->inventory_infinite_element = element;
9823                 else
9824                   for (k = 0; k < collect_count; k++)
9825                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9826                       player->inventory_element[player->inventory_size++] =
9827                         element;
9828               }
9829             }
9830           }
9831         }
9832       }
9833
9834       break;
9835     }
9836
9837     /* ---------- CE actions  ---------------------------------------------- */
9838
9839     case CA_SET_CE_VALUE:
9840     {
9841       int last_ce_value = CustomValue[x][y];
9842
9843       CustomValue[x][y] = action_arg_number_new;
9844
9845       if (CustomValue[x][y] != last_ce_value)
9846       {
9847         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9848         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9849
9850         if (CustomValue[x][y] == 0)
9851         {
9852           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9853           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9854         }
9855       }
9856
9857       break;
9858     }
9859
9860     case CA_SET_CE_SCORE:
9861     {
9862       int last_ce_score = ei->collect_score;
9863
9864       ei->collect_score = action_arg_number_new;
9865
9866       if (ei->collect_score != last_ce_score)
9867       {
9868         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9869         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9870
9871         if (ei->collect_score == 0)
9872         {
9873           int xx, yy;
9874
9875           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9876           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9877
9878           /*
9879             This is a very special case that seems to be a mixture between
9880             CheckElementChange() and CheckTriggeredElementChange(): while
9881             the first one only affects single elements that are triggered
9882             directly, the second one affects multiple elements in the playfield
9883             that are triggered indirectly by another element. This is a third
9884             case: Changing the CE score always affects multiple identical CEs,
9885             so every affected CE must be checked, not only the single CE for
9886             which the CE score was changed in the first place (as every instance
9887             of that CE shares the same CE score, and therefore also can change)!
9888           */
9889           SCAN_PLAYFIELD(xx, yy)
9890           {
9891             if (Feld[xx][yy] == element)
9892               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9893                                  CE_SCORE_GETS_ZERO);
9894           }
9895         }
9896       }
9897
9898       break;
9899     }
9900
9901     case CA_SET_CE_ARTWORK:
9902     {
9903       int artwork_element = action_arg_element;
9904       boolean reset_frame = FALSE;
9905       int xx, yy;
9906
9907       if (action_arg == CA_ARG_ELEMENT_RESET)
9908         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9909                            element);
9910
9911       if (ei->gfx_element != artwork_element)
9912         reset_frame = TRUE;
9913
9914       ei->gfx_element = artwork_element;
9915
9916       SCAN_PLAYFIELD(xx, yy)
9917       {
9918         if (Feld[xx][yy] == element)
9919         {
9920           if (reset_frame)
9921           {
9922             ResetGfxAnimation(xx, yy);
9923             ResetRandomAnimationValue(xx, yy);
9924           }
9925
9926           TEST_DrawLevelField(xx, yy);
9927         }
9928       }
9929
9930       break;
9931     }
9932
9933     /* ---------- engine actions  ------------------------------------------ */
9934
9935     case CA_SET_ENGINE_SCAN_MODE:
9936     {
9937       InitPlayfieldScanMode(action_arg);
9938
9939       break;
9940     }
9941
9942     default:
9943       break;
9944   }
9945 }
9946
9947 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9948 {
9949   int old_element = Feld[x][y];
9950   int new_element = GetElementFromGroupElement(element);
9951   int previous_move_direction = MovDir[x][y];
9952   int last_ce_value = CustomValue[x][y];
9953   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9954   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9955   boolean add_player_onto_element = (new_element_is_player &&
9956                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9957                                      IS_WALKABLE(old_element));
9958
9959   if (!add_player_onto_element)
9960   {
9961     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9962       RemoveMovingField(x, y);
9963     else
9964       RemoveField(x, y);
9965
9966     Feld[x][y] = new_element;
9967
9968     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9969       MovDir[x][y] = previous_move_direction;
9970
9971     if (element_info[new_element].use_last_ce_value)
9972       CustomValue[x][y] = last_ce_value;
9973
9974     InitField_WithBug1(x, y, FALSE);
9975
9976     new_element = Feld[x][y];   /* element may have changed */
9977
9978     ResetGfxAnimation(x, y);
9979     ResetRandomAnimationValue(x, y);
9980
9981     TEST_DrawLevelField(x, y);
9982
9983     if (GFX_CRUMBLED(new_element))
9984       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9985   }
9986
9987   /* check if element under the player changes from accessible to unaccessible
9988      (needed for special case of dropping element which then changes) */
9989   /* (must be checked after creating new element for walkable group elements) */
9990   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9991       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9992   {
9993     Bang(x, y);
9994
9995     return;
9996   }
9997
9998   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9999   if (new_element_is_player)
10000     RelocatePlayer(x, y, new_element);
10001
10002   if (is_change)
10003     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10004
10005   TestIfBadThingTouchesPlayer(x, y);
10006   TestIfPlayerTouchesCustomElement(x, y);
10007   TestIfElementTouchesCustomElement(x, y);
10008 }
10009
10010 static void CreateField(int x, int y, int element)
10011 {
10012   CreateFieldExt(x, y, element, FALSE);
10013 }
10014
10015 static void CreateElementFromChange(int x, int y, int element)
10016 {
10017   element = GET_VALID_RUNTIME_ELEMENT(element);
10018
10019   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10020   {
10021     int old_element = Feld[x][y];
10022
10023     /* prevent changed element from moving in same engine frame
10024        unless both old and new element can either fall or move */
10025     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10026         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10027       Stop[x][y] = TRUE;
10028   }
10029
10030   CreateFieldExt(x, y, element, TRUE);
10031 }
10032
10033 static boolean ChangeElement(int x, int y, int element, int page)
10034 {
10035   struct ElementInfo *ei = &element_info[element];
10036   struct ElementChangeInfo *change = &ei->change_page[page];
10037   int ce_value = CustomValue[x][y];
10038   int ce_score = ei->collect_score;
10039   int target_element;
10040   int old_element = Feld[x][y];
10041
10042   /* always use default change event to prevent running into a loop */
10043   if (ChangeEvent[x][y] == -1)
10044     ChangeEvent[x][y] = CE_DELAY;
10045
10046   if (ChangeEvent[x][y] == CE_DELAY)
10047   {
10048     /* reset actual trigger element, trigger player and action element */
10049     change->actual_trigger_element = EL_EMPTY;
10050     change->actual_trigger_player = EL_EMPTY;
10051     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10052     change->actual_trigger_side = CH_SIDE_NONE;
10053     change->actual_trigger_ce_value = 0;
10054     change->actual_trigger_ce_score = 0;
10055   }
10056
10057   /* do not change elements more than a specified maximum number of changes */
10058   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10059     return FALSE;
10060
10061   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10062
10063   if (change->explode)
10064   {
10065     Bang(x, y);
10066
10067     return TRUE;
10068   }
10069
10070   if (change->use_target_content)
10071   {
10072     boolean complete_replace = TRUE;
10073     boolean can_replace[3][3];
10074     int xx, yy;
10075
10076     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10077     {
10078       boolean is_empty;
10079       boolean is_walkable;
10080       boolean is_diggable;
10081       boolean is_collectible;
10082       boolean is_removable;
10083       boolean is_destructible;
10084       int ex = x + xx - 1;
10085       int ey = y + yy - 1;
10086       int content_element = change->target_content.e[xx][yy];
10087       int e;
10088
10089       can_replace[xx][yy] = TRUE;
10090
10091       if (ex == x && ey == y)   /* do not check changing element itself */
10092         continue;
10093
10094       if (content_element == EL_EMPTY_SPACE)
10095       {
10096         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10097
10098         continue;
10099       }
10100
10101       if (!IN_LEV_FIELD(ex, ey))
10102       {
10103         can_replace[xx][yy] = FALSE;
10104         complete_replace = FALSE;
10105
10106         continue;
10107       }
10108
10109       e = Feld[ex][ey];
10110
10111       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10112         e = MovingOrBlocked2Element(ex, ey);
10113
10114       is_empty = (IS_FREE(ex, ey) ||
10115                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10116
10117       is_walkable     = (is_empty || IS_WALKABLE(e));
10118       is_diggable     = (is_empty || IS_DIGGABLE(e));
10119       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10120       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10121       is_removable    = (is_diggable || is_collectible);
10122
10123       can_replace[xx][yy] =
10124         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10125           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10126           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10127           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10128           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10129           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10130          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10131
10132       if (!can_replace[xx][yy])
10133         complete_replace = FALSE;
10134     }
10135
10136     if (!change->only_if_complete || complete_replace)
10137     {
10138       boolean something_has_changed = FALSE;
10139
10140       if (change->only_if_complete && change->use_random_replace &&
10141           RND(100) < change->random_percentage)
10142         return FALSE;
10143
10144       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10145       {
10146         int ex = x + xx - 1;
10147         int ey = y + yy - 1;
10148         int content_element;
10149
10150         if (can_replace[xx][yy] && (!change->use_random_replace ||
10151                                     RND(100) < change->random_percentage))
10152         {
10153           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10154             RemoveMovingField(ex, ey);
10155
10156           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10157
10158           content_element = change->target_content.e[xx][yy];
10159           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10160                                               ce_value, ce_score);
10161
10162           CreateElementFromChange(ex, ey, target_element);
10163
10164           something_has_changed = TRUE;
10165
10166           /* for symmetry reasons, freeze newly created border elements */
10167           if (ex != x || ey != y)
10168             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10169         }
10170       }
10171
10172       if (something_has_changed)
10173       {
10174         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10175         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10176       }
10177     }
10178   }
10179   else
10180   {
10181     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10182                                         ce_value, ce_score);
10183
10184     if (element == EL_DIAGONAL_GROWING ||
10185         element == EL_DIAGONAL_SHRINKING)
10186     {
10187       target_element = Store[x][y];
10188
10189       Store[x][y] = EL_EMPTY;
10190     }
10191
10192     CreateElementFromChange(x, y, target_element);
10193
10194     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10195     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10196   }
10197
10198   /* this uses direct change before indirect change */
10199   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10200
10201   return TRUE;
10202 }
10203
10204 static void HandleElementChange(int x, int y, int page)
10205 {
10206   int element = MovingOrBlocked2Element(x, y);
10207   struct ElementInfo *ei = &element_info[element];
10208   struct ElementChangeInfo *change = &ei->change_page[page];
10209   boolean handle_action_before_change = FALSE;
10210
10211 #ifdef DEBUG
10212   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10213       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10214   {
10215     printf("\n\n");
10216     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10217            x, y, element, element_info[element].token_name);
10218     printf("HandleElementChange(): This should never happen!\n");
10219     printf("\n\n");
10220   }
10221 #endif
10222
10223   /* this can happen with classic bombs on walkable, changing elements */
10224   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10225   {
10226     return;
10227   }
10228
10229   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10230   {
10231     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10232
10233     if (change->can_change)
10234     {
10235       /* !!! not clear why graphic animation should be reset at all here !!! */
10236       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10237       /* when a custom element is about to change (for example by change delay),
10238          do not reset graphic animation when the custom element is moving */
10239       if (!IS_MOVING(x, y))
10240       {
10241         ResetGfxAnimation(x, y);
10242         ResetRandomAnimationValue(x, y);
10243       }
10244
10245       if (change->pre_change_function)
10246         change->pre_change_function(x, y);
10247     }
10248   }
10249
10250   ChangeDelay[x][y]--;
10251
10252   if (ChangeDelay[x][y] != 0)           /* continue element change */
10253   {
10254     if (change->can_change)
10255     {
10256       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10257
10258       if (IS_ANIMATED(graphic))
10259         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10260
10261       if (change->change_function)
10262         change->change_function(x, y);
10263     }
10264   }
10265   else                                  /* finish element change */
10266   {
10267     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10268     {
10269       page = ChangePage[x][y];
10270       ChangePage[x][y] = -1;
10271
10272       change = &ei->change_page[page];
10273     }
10274
10275     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10276     {
10277       ChangeDelay[x][y] = 1;            /* try change after next move step */
10278       ChangePage[x][y] = page;          /* remember page to use for change */
10279
10280       return;
10281     }
10282
10283     /* special case: set new level random seed before changing element */
10284     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10285       handle_action_before_change = TRUE;
10286
10287     if (change->has_action && handle_action_before_change)
10288       ExecuteCustomElementAction(x, y, element, page);
10289
10290     if (change->can_change)
10291     {
10292       if (ChangeElement(x, y, element, page))
10293       {
10294         if (change->post_change_function)
10295           change->post_change_function(x, y);
10296       }
10297     }
10298
10299     if (change->has_action && !handle_action_before_change)
10300       ExecuteCustomElementAction(x, y, element, page);
10301   }
10302 }
10303
10304 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10305                                               int trigger_element,
10306                                               int trigger_event,
10307                                               int trigger_player,
10308                                               int trigger_side,
10309                                               int trigger_page)
10310 {
10311   boolean change_done_any = FALSE;
10312   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10313   int i;
10314
10315   if (!(trigger_events[trigger_element][trigger_event]))
10316     return FALSE;
10317
10318   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10319
10320   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10321   {
10322     int element = EL_CUSTOM_START + i;
10323     boolean change_done = FALSE;
10324     int p;
10325
10326     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10327         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10328       continue;
10329
10330     for (p = 0; p < element_info[element].num_change_pages; p++)
10331     {
10332       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10333
10334       if (change->can_change_or_has_action &&
10335           change->has_event[trigger_event] &&
10336           change->trigger_side & trigger_side &&
10337           change->trigger_player & trigger_player &&
10338           change->trigger_page & trigger_page_bits &&
10339           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10340       {
10341         change->actual_trigger_element = trigger_element;
10342         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10343         change->actual_trigger_player_bits = trigger_player;
10344         change->actual_trigger_side = trigger_side;
10345         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10346         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10347
10348         if ((change->can_change && !change_done) || change->has_action)
10349         {
10350           int x, y;
10351
10352           SCAN_PLAYFIELD(x, y)
10353           {
10354             if (Feld[x][y] == element)
10355             {
10356               if (change->can_change && !change_done)
10357               {
10358                 /* if element already changed in this frame, not only prevent
10359                    another element change (checked in ChangeElement()), but
10360                    also prevent additional element actions for this element */
10361
10362                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10363                     !level.use_action_after_change_bug)
10364                   continue;
10365
10366                 ChangeDelay[x][y] = 1;
10367                 ChangeEvent[x][y] = trigger_event;
10368
10369                 HandleElementChange(x, y, p);
10370               }
10371               else if (change->has_action)
10372               {
10373                 /* if element already changed in this frame, not only prevent
10374                    another element change (checked in ChangeElement()), but
10375                    also prevent additional element actions for this element */
10376
10377                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10378                     !level.use_action_after_change_bug)
10379                   continue;
10380
10381                 ExecuteCustomElementAction(x, y, element, p);
10382                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10383               }
10384             }
10385           }
10386
10387           if (change->can_change)
10388           {
10389             change_done = TRUE;
10390             change_done_any = TRUE;
10391           }
10392         }
10393       }
10394     }
10395   }
10396
10397   RECURSION_LOOP_DETECTION_END();
10398
10399   return change_done_any;
10400 }
10401
10402 static boolean CheckElementChangeExt(int x, int y,
10403                                      int element,
10404                                      int trigger_element,
10405                                      int trigger_event,
10406                                      int trigger_player,
10407                                      int trigger_side)
10408 {
10409   boolean change_done = FALSE;
10410   int p;
10411
10412   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10413       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10414     return FALSE;
10415
10416   if (Feld[x][y] == EL_BLOCKED)
10417   {
10418     Blocked2Moving(x, y, &x, &y);
10419     element = Feld[x][y];
10420   }
10421
10422   /* check if element has already changed or is about to change after moving */
10423   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10424        Feld[x][y] != element) ||
10425
10426       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10427        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10428         ChangePage[x][y] != -1)))
10429     return FALSE;
10430
10431   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10432
10433   for (p = 0; p < element_info[element].num_change_pages; p++)
10434   {
10435     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10436
10437     /* check trigger element for all events where the element that is checked
10438        for changing interacts with a directly adjacent element -- this is
10439        different to element changes that affect other elements to change on the
10440        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10441     boolean check_trigger_element =
10442       (trigger_event == CE_TOUCHING_X ||
10443        trigger_event == CE_HITTING_X ||
10444        trigger_event == CE_HIT_BY_X ||
10445        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10446
10447     if (change->can_change_or_has_action &&
10448         change->has_event[trigger_event] &&
10449         change->trigger_side & trigger_side &&
10450         change->trigger_player & trigger_player &&
10451         (!check_trigger_element ||
10452          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10453     {
10454       change->actual_trigger_element = trigger_element;
10455       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10456       change->actual_trigger_player_bits = trigger_player;
10457       change->actual_trigger_side = trigger_side;
10458       change->actual_trigger_ce_value = CustomValue[x][y];
10459       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10460
10461       /* special case: trigger element not at (x,y) position for some events */
10462       if (check_trigger_element)
10463       {
10464         static struct
10465         {
10466           int dx, dy;
10467         } move_xy[] =
10468           {
10469             {  0,  0 },
10470             { -1,  0 },
10471             { +1,  0 },
10472             {  0,  0 },
10473             {  0, -1 },
10474             {  0,  0 }, { 0, 0 }, { 0, 0 },
10475             {  0, +1 }
10476           };
10477
10478         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10479         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10480
10481         change->actual_trigger_ce_value = CustomValue[xx][yy];
10482         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10483       }
10484
10485       if (change->can_change && !change_done)
10486       {
10487         ChangeDelay[x][y] = 1;
10488         ChangeEvent[x][y] = trigger_event;
10489
10490         HandleElementChange(x, y, p);
10491
10492         change_done = TRUE;
10493       }
10494       else if (change->has_action)
10495       {
10496         ExecuteCustomElementAction(x, y, element, p);
10497         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10498       }
10499     }
10500   }
10501
10502   RECURSION_LOOP_DETECTION_END();
10503
10504   return change_done;
10505 }
10506
10507 static void PlayPlayerSound(struct PlayerInfo *player)
10508 {
10509   int jx = player->jx, jy = player->jy;
10510   int sound_element = player->artwork_element;
10511   int last_action = player->last_action_waiting;
10512   int action = player->action_waiting;
10513
10514   if (player->is_waiting)
10515   {
10516     if (action != last_action)
10517       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10518     else
10519       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10520   }
10521   else
10522   {
10523     if (action != last_action)
10524       StopSound(element_info[sound_element].sound[last_action]);
10525
10526     if (last_action == ACTION_SLEEPING)
10527       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10528   }
10529 }
10530
10531 static void PlayAllPlayersSound()
10532 {
10533   int i;
10534
10535   for (i = 0; i < MAX_PLAYERS; i++)
10536     if (stored_player[i].active)
10537       PlayPlayerSound(&stored_player[i]);
10538 }
10539
10540 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10541 {
10542   boolean last_waiting = player->is_waiting;
10543   int move_dir = player->MovDir;
10544
10545   player->dir_waiting = move_dir;
10546   player->last_action_waiting = player->action_waiting;
10547
10548   if (is_waiting)
10549   {
10550     if (!last_waiting)          /* not waiting -> waiting */
10551     {
10552       player->is_waiting = TRUE;
10553
10554       player->frame_counter_bored =
10555         FrameCounter +
10556         game.player_boring_delay_fixed +
10557         GetSimpleRandom(game.player_boring_delay_random);
10558       player->frame_counter_sleeping =
10559         FrameCounter +
10560         game.player_sleeping_delay_fixed +
10561         GetSimpleRandom(game.player_sleeping_delay_random);
10562
10563       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10564     }
10565
10566     if (game.player_sleeping_delay_fixed +
10567         game.player_sleeping_delay_random > 0 &&
10568         player->anim_delay_counter == 0 &&
10569         player->post_delay_counter == 0 &&
10570         FrameCounter >= player->frame_counter_sleeping)
10571       player->is_sleeping = TRUE;
10572     else if (game.player_boring_delay_fixed +
10573              game.player_boring_delay_random > 0 &&
10574              FrameCounter >= player->frame_counter_bored)
10575       player->is_bored = TRUE;
10576
10577     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10578                               player->is_bored ? ACTION_BORING :
10579                               ACTION_WAITING);
10580
10581     if (player->is_sleeping && player->use_murphy)
10582     {
10583       /* special case for sleeping Murphy when leaning against non-free tile */
10584
10585       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10586           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10587            !IS_MOVING(player->jx - 1, player->jy)))
10588         move_dir = MV_LEFT;
10589       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10590                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10591                 !IS_MOVING(player->jx + 1, player->jy)))
10592         move_dir = MV_RIGHT;
10593       else
10594         player->is_sleeping = FALSE;
10595
10596       player->dir_waiting = move_dir;
10597     }
10598
10599     if (player->is_sleeping)
10600     {
10601       if (player->num_special_action_sleeping > 0)
10602       {
10603         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10604         {
10605           int last_special_action = player->special_action_sleeping;
10606           int num_special_action = player->num_special_action_sleeping;
10607           int special_action =
10608             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10609              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10610              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10611              last_special_action + 1 : ACTION_SLEEPING);
10612           int special_graphic =
10613             el_act_dir2img(player->artwork_element, special_action, move_dir);
10614
10615           player->anim_delay_counter =
10616             graphic_info[special_graphic].anim_delay_fixed +
10617             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10618           player->post_delay_counter =
10619             graphic_info[special_graphic].post_delay_fixed +
10620             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10621
10622           player->special_action_sleeping = special_action;
10623         }
10624
10625         if (player->anim_delay_counter > 0)
10626         {
10627           player->action_waiting = player->special_action_sleeping;
10628           player->anim_delay_counter--;
10629         }
10630         else if (player->post_delay_counter > 0)
10631         {
10632           player->post_delay_counter--;
10633         }
10634       }
10635     }
10636     else if (player->is_bored)
10637     {
10638       if (player->num_special_action_bored > 0)
10639       {
10640         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10641         {
10642           int special_action =
10643             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10644           int special_graphic =
10645             el_act_dir2img(player->artwork_element, special_action, move_dir);
10646
10647           player->anim_delay_counter =
10648             graphic_info[special_graphic].anim_delay_fixed +
10649             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10650           player->post_delay_counter =
10651             graphic_info[special_graphic].post_delay_fixed +
10652             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10653
10654           player->special_action_bored = special_action;
10655         }
10656
10657         if (player->anim_delay_counter > 0)
10658         {
10659           player->action_waiting = player->special_action_bored;
10660           player->anim_delay_counter--;
10661         }
10662         else if (player->post_delay_counter > 0)
10663         {
10664           player->post_delay_counter--;
10665         }
10666       }
10667     }
10668   }
10669   else if (last_waiting)        /* waiting -> not waiting */
10670   {
10671     player->is_waiting = FALSE;
10672     player->is_bored = FALSE;
10673     player->is_sleeping = FALSE;
10674
10675     player->frame_counter_bored = -1;
10676     player->frame_counter_sleeping = -1;
10677
10678     player->anim_delay_counter = 0;
10679     player->post_delay_counter = 0;
10680
10681     player->dir_waiting = player->MovDir;
10682     player->action_waiting = ACTION_DEFAULT;
10683
10684     player->special_action_bored = ACTION_DEFAULT;
10685     player->special_action_sleeping = ACTION_DEFAULT;
10686   }
10687 }
10688
10689 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10690 {
10691   static boolean player_was_moving = FALSE;
10692   static boolean player_was_snapping = FALSE;
10693   static boolean player_was_dropping = FALSE;
10694
10695   if ((!player->is_moving  && player_was_moving) ||
10696       (player->MovPos == 0 && player_was_moving) ||
10697       (player->is_snapping && !player_was_snapping) ||
10698       (player->is_dropping && !player_was_dropping))
10699   {
10700     if (!SaveEngineSnapshotToList())
10701       return;
10702
10703     player_was_moving = FALSE;
10704     player_was_snapping = TRUE;
10705     player_was_dropping = TRUE;
10706   }
10707   else
10708   {
10709     if (player->is_moving)
10710       player_was_moving = TRUE;
10711
10712     if (!player->is_snapping)
10713       player_was_snapping = FALSE;
10714
10715     if (!player->is_dropping)
10716       player_was_dropping = FALSE;
10717   }
10718 }
10719
10720 static void CheckSingleStepMode(struct PlayerInfo *player)
10721 {
10722   if (tape.single_step && tape.recording && !tape.pausing)
10723   {
10724     /* as it is called "single step mode", just return to pause mode when the
10725        player stopped moving after one tile (or never starts moving at all) */
10726     if (!player->is_moving && !player->is_pushing)
10727     {
10728       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10729       SnapField(player, 0, 0);                  /* stop snapping */
10730     }
10731   }
10732
10733   CheckSaveEngineSnapshot(player);
10734 }
10735
10736 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10737 {
10738   int left      = player_action & JOY_LEFT;
10739   int right     = player_action & JOY_RIGHT;
10740   int up        = player_action & JOY_UP;
10741   int down      = player_action & JOY_DOWN;
10742   int button1   = player_action & JOY_BUTTON_1;
10743   int button2   = player_action & JOY_BUTTON_2;
10744   int dx        = (left ? -1 : right ? 1 : 0);
10745   int dy        = (up   ? -1 : down  ? 1 : 0);
10746
10747   if (!player->active || tape.pausing)
10748     return 0;
10749
10750   if (player_action)
10751   {
10752     if (button1)
10753       SnapField(player, dx, dy);
10754     else
10755     {
10756       if (button2)
10757         DropElement(player);
10758
10759       MovePlayer(player, dx, dy);
10760     }
10761
10762     CheckSingleStepMode(player);
10763
10764     SetPlayerWaiting(player, FALSE);
10765
10766     return player_action;
10767   }
10768   else
10769   {
10770     /* no actions for this player (no input at player's configured device) */
10771
10772     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10773     SnapField(player, 0, 0);
10774     CheckGravityMovementWhenNotMoving(player);
10775
10776     if (player->MovPos == 0)
10777       SetPlayerWaiting(player, TRUE);
10778
10779     if (player->MovPos == 0)    /* needed for tape.playing */
10780       player->is_moving = FALSE;
10781
10782     player->is_dropping = FALSE;
10783     player->is_dropping_pressed = FALSE;
10784     player->drop_pressed_delay = 0;
10785
10786     CheckSingleStepMode(player);
10787
10788     return 0;
10789   }
10790 }
10791
10792 static void CheckLevelTime()
10793 {
10794   int i;
10795
10796   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10797   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10798   {
10799     if (level.native_em_level->lev->home == 0)  /* all players at home */
10800     {
10801       PlayerWins(local_player);
10802
10803       AllPlayersGone = TRUE;
10804
10805       level.native_em_level->lev->home = -1;
10806     }
10807
10808     if (level.native_em_level->ply[0]->alive == 0 &&
10809         level.native_em_level->ply[1]->alive == 0 &&
10810         level.native_em_level->ply[2]->alive == 0 &&
10811         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10812       AllPlayersGone = TRUE;
10813   }
10814   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10815   {
10816     if (game_sp.LevelSolved &&
10817         !game_sp.GameOver)                              /* game won */
10818     {
10819       PlayerWins(local_player);
10820
10821       game_sp.GameOver = TRUE;
10822
10823       AllPlayersGone = TRUE;
10824     }
10825
10826     if (game_sp.GameOver)                               /* game lost */
10827       AllPlayersGone = TRUE;
10828   }
10829
10830   if (TimeFrames >= FRAMES_PER_SECOND)
10831   {
10832     TimeFrames = 0;
10833     TapeTime++;
10834
10835     for (i = 0; i < MAX_PLAYERS; i++)
10836     {
10837       struct PlayerInfo *player = &stored_player[i];
10838
10839       if (SHIELD_ON(player))
10840       {
10841         player->shield_normal_time_left--;
10842
10843         if (player->shield_deadly_time_left > 0)
10844           player->shield_deadly_time_left--;
10845       }
10846     }
10847
10848     if (!local_player->LevelSolved && !level.use_step_counter)
10849     {
10850       TimePlayed++;
10851
10852       if (TimeLeft > 0)
10853       {
10854         TimeLeft--;
10855
10856         if (TimeLeft <= 10 && setup.time_limit)
10857           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10858
10859         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10860            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10861
10862         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10863
10864         if (!TimeLeft && setup.time_limit)
10865         {
10866           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10867             level.native_em_level->lev->killed_out_of_time = TRUE;
10868           else
10869             for (i = 0; i < MAX_PLAYERS; i++)
10870               KillPlayer(&stored_player[i]);
10871         }
10872       }
10873       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10874       {
10875         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10876       }
10877
10878       level.native_em_level->lev->time =
10879         (game.no_time_limit ? TimePlayed : TimeLeft);
10880     }
10881
10882     if (tape.recording || tape.playing)
10883       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10884   }
10885
10886   if (tape.recording || tape.playing)
10887     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10888
10889   UpdateAndDisplayGameControlValues();
10890 }
10891
10892 void AdvanceFrameAndPlayerCounters(int player_nr)
10893 {
10894   int i;
10895
10896   /* advance frame counters (global frame counter and time frame counter) */
10897   FrameCounter++;
10898   TimeFrames++;
10899
10900   /* advance player counters (counters for move delay, move animation etc.) */
10901   for (i = 0; i < MAX_PLAYERS; i++)
10902   {
10903     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10904     int move_delay_value = stored_player[i].move_delay_value;
10905     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10906
10907     if (!advance_player_counters)       /* not all players may be affected */
10908       continue;
10909
10910     if (move_frames == 0)       /* less than one move per game frame */
10911     {
10912       int stepsize = TILEX / move_delay_value;
10913       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10914       int count = (stored_player[i].is_moving ?
10915                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10916
10917       if (count % delay == 0)
10918         move_frames = 1;
10919     }
10920
10921     stored_player[i].Frame += move_frames;
10922
10923     if (stored_player[i].MovPos != 0)
10924       stored_player[i].StepFrame += move_frames;
10925
10926     if (stored_player[i].move_delay > 0)
10927       stored_player[i].move_delay--;
10928
10929     /* due to bugs in previous versions, counter must count up, not down */
10930     if (stored_player[i].push_delay != -1)
10931       stored_player[i].push_delay++;
10932
10933     if (stored_player[i].drop_delay > 0)
10934       stored_player[i].drop_delay--;
10935
10936     if (stored_player[i].is_dropping_pressed)
10937       stored_player[i].drop_pressed_delay++;
10938   }
10939 }
10940
10941 void StartGameActions(boolean init_network_game, boolean record_tape,
10942                       int random_seed)
10943 {
10944   unsigned int new_random_seed = InitRND(random_seed);
10945
10946   if (record_tape)
10947     TapeStartRecording(new_random_seed);
10948
10949 #if defined(NETWORK_AVALIABLE)
10950   if (init_network_game)
10951   {
10952     SendToServer_StartPlaying();
10953
10954     return;
10955   }
10956 #endif
10957
10958   InitGame();
10959 }
10960
10961 void GameActions()
10962 {
10963   static unsigned int game_frame_delay = 0;
10964   unsigned int game_frame_delay_value;
10965   byte *recorded_player_action;
10966   byte summarized_player_action = 0;
10967   byte tape_action[MAX_PLAYERS];
10968   int i;
10969
10970   for (i = 0; i < MAX_PLAYERS; i++)
10971   {
10972     struct PlayerInfo *player = &stored_player[i];
10973
10974     // allow engine snapshot if movement attempt was stopped
10975     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10976         (player->action & KEY_MOTION) == 0)
10977       game.snapshot.changed_action = TRUE;
10978
10979     // allow engine snapshot in case of snapping/dropping attempt
10980     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10981         (player->action & KEY_BUTTON) != 0)
10982       game.snapshot.changed_action = TRUE;
10983
10984     game.snapshot.last_action[i] = player->action;
10985   }
10986
10987   /* detect endless loops, caused by custom element programming */
10988   if (recursion_loop_detected && recursion_loop_depth == 0)
10989   {
10990     char *message = getStringCat3("Internal Error! Element ",
10991                                   EL_NAME(recursion_loop_element),
10992                                   " caused endless loop! Quit the game?");
10993
10994     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10995           EL_NAME(recursion_loop_element));
10996
10997     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10998
10999     recursion_loop_detected = FALSE;    /* if game should be continued */
11000
11001     free(message);
11002
11003     return;
11004   }
11005
11006   if (game.restart_level)
11007     StartGameActions(options.network, setup.autorecord, level.random_seed);
11008
11009   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11010   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11011   {
11012     if (level.native_em_level->lev->home == 0)  /* all players at home */
11013     {
11014       PlayerWins(local_player);
11015
11016       AllPlayersGone = TRUE;
11017
11018       level.native_em_level->lev->home = -1;
11019     }
11020
11021     if (level.native_em_level->ply[0]->alive == 0 &&
11022         level.native_em_level->ply[1]->alive == 0 &&
11023         level.native_em_level->ply[2]->alive == 0 &&
11024         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11025       AllPlayersGone = TRUE;
11026   }
11027   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11028   {
11029     if (game_sp.LevelSolved &&
11030         !game_sp.GameOver)                              /* game won */
11031     {
11032       PlayerWins(local_player);
11033
11034       game_sp.GameOver = TRUE;
11035
11036       AllPlayersGone = TRUE;
11037     }
11038
11039     if (game_sp.GameOver)                               /* game lost */
11040       AllPlayersGone = TRUE;
11041   }
11042
11043   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11044     GameWon();
11045
11046   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11047     TapeStop();
11048
11049   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11050     return;
11051
11052   game_frame_delay_value =
11053     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11054
11055   if (tape.playing && tape.warp_forward && !tape.pausing)
11056     game_frame_delay_value = 0;
11057
11058   /* ---------- main game synchronization point ---------- */
11059
11060   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11061
11062   if (network_playing && !network_player_action_received)
11063   {
11064     /* try to get network player actions in time */
11065
11066 #if defined(NETWORK_AVALIABLE)
11067     /* last chance to get network player actions without main loop delay */
11068     HandleNetworking();
11069 #endif
11070
11071     /* game was quit by network peer */
11072     if (game_status != GAME_MODE_PLAYING)
11073       return;
11074
11075     if (!network_player_action_received)
11076       return;           /* failed to get network player actions in time */
11077
11078     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11079   }
11080
11081   if (tape.pausing)
11082     return;
11083
11084   /* at this point we know that we really continue executing the game */
11085
11086   network_player_action_received = FALSE;
11087
11088   /* when playing tape, read previously recorded player input from tape data */
11089   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11090
11091   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11092   if (tape.pausing)
11093     return;
11094
11095   if (tape.set_centered_player)
11096   {
11097     game.centered_player_nr_next = tape.centered_player_nr_next;
11098     game.set_centered_player = TRUE;
11099   }
11100
11101   for (i = 0; i < MAX_PLAYERS; i++)
11102   {
11103     summarized_player_action |= stored_player[i].action;
11104
11105     if (!network_playing && (game.team_mode || tape.playing))
11106       stored_player[i].effective_action = stored_player[i].action;
11107   }
11108
11109 #if defined(NETWORK_AVALIABLE)
11110   if (network_playing)
11111     SendToServer_MovePlayer(summarized_player_action);
11112 #endif
11113
11114   if (!options.network && !game.team_mode)
11115     local_player->effective_action = summarized_player_action;
11116
11117   if (tape.recording &&
11118       setup.team_mode &&
11119       setup.input_on_focus &&
11120       game.centered_player_nr != -1)
11121   {
11122     for (i = 0; i < MAX_PLAYERS; i++)
11123       stored_player[i].effective_action =
11124         (i == game.centered_player_nr ? summarized_player_action : 0);
11125   }
11126
11127   if (recorded_player_action != NULL)
11128     for (i = 0; i < MAX_PLAYERS; i++)
11129       stored_player[i].effective_action = recorded_player_action[i];
11130
11131   for (i = 0; i < MAX_PLAYERS; i++)
11132   {
11133     tape_action[i] = stored_player[i].effective_action;
11134
11135     /* (this may happen in the RND game engine if a player was not present on
11136        the playfield on level start, but appeared later from a custom element */
11137     if (setup.team_mode &&
11138         tape.recording &&
11139         tape_action[i] &&
11140         !tape.player_participates[i])
11141       tape.player_participates[i] = TRUE;
11142   }
11143
11144   /* only record actions from input devices, but not programmed actions */
11145   if (tape.recording)
11146     TapeRecordAction(tape_action);
11147
11148 #if USE_NEW_PLAYER_ASSIGNMENTS
11149   // !!! also map player actions in single player mode !!!
11150   // if (game.team_mode)
11151   {
11152     byte mapped_action[MAX_PLAYERS];
11153
11154 #if DEBUG_PLAYER_ACTIONS
11155     printf(":::");
11156     for (i = 0; i < MAX_PLAYERS; i++)
11157       printf(" %d, ", stored_player[i].effective_action);
11158 #endif
11159
11160     for (i = 0; i < MAX_PLAYERS; i++)
11161       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11162
11163     for (i = 0; i < MAX_PLAYERS; i++)
11164       stored_player[i].effective_action = mapped_action[i];
11165
11166 #if DEBUG_PLAYER_ACTIONS
11167     printf(" =>");
11168     for (i = 0; i < MAX_PLAYERS; i++)
11169       printf(" %d, ", stored_player[i].effective_action);
11170     printf("\n");
11171 #endif
11172   }
11173 #if DEBUG_PLAYER_ACTIONS
11174   else
11175   {
11176     printf(":::");
11177     for (i = 0; i < MAX_PLAYERS; i++)
11178       printf(" %d, ", stored_player[i].effective_action);
11179     printf("\n");
11180   }
11181 #endif
11182 #endif
11183
11184   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11185   {
11186     GameActions_EM_Main();
11187   }
11188   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11189   {
11190     GameActions_SP_Main();
11191   }
11192   else
11193   {
11194     GameActions_RND_Main();
11195   }
11196
11197   redraw_mask |= REDRAW_FIELD;
11198 }
11199
11200 void GameActions_EM_Main()
11201 {
11202   byte effective_action[MAX_PLAYERS];
11203   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11204   int i;
11205
11206   for (i = 0; i < MAX_PLAYERS; i++)
11207     effective_action[i] = stored_player[i].effective_action;
11208
11209   GameActions_EM(effective_action, warp_mode);
11210
11211   CheckLevelTime();
11212
11213   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11214 }
11215
11216 void GameActions_SP_Main()
11217 {
11218   byte effective_action[MAX_PLAYERS];
11219   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11220   int i;
11221
11222   for (i = 0; i < MAX_PLAYERS; i++)
11223     effective_action[i] = stored_player[i].effective_action;
11224
11225   GameActions_SP(effective_action, warp_mode);
11226
11227   CheckLevelTime();
11228
11229   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11230 }
11231
11232 void GameActions_RND_Main()
11233 {
11234   GameActions_RND();
11235
11236   BlitScreenToBitmap_RND(backbuffer);
11237 }
11238
11239 void GameActions_RND()
11240 {
11241   int magic_wall_x = 0, magic_wall_y = 0;
11242   int i, x, y, element, graphic;
11243
11244   InitPlayfieldScanModeVars();
11245
11246   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11247   {
11248     SCAN_PLAYFIELD(x, y)
11249     {
11250       ChangeCount[x][y] = 0;
11251       ChangeEvent[x][y] = -1;
11252     }
11253   }
11254
11255   if (game.set_centered_player)
11256   {
11257     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11258
11259     /* switching to "all players" only possible if all players fit to screen */
11260     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11261     {
11262       game.centered_player_nr_next = game.centered_player_nr;
11263       game.set_centered_player = FALSE;
11264     }
11265
11266     /* do not switch focus to non-existing (or non-active) player */
11267     if (game.centered_player_nr_next >= 0 &&
11268         !stored_player[game.centered_player_nr_next].active)
11269     {
11270       game.centered_player_nr_next = game.centered_player_nr;
11271       game.set_centered_player = FALSE;
11272     }
11273   }
11274
11275   if (game.set_centered_player &&
11276       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11277   {
11278     int sx, sy;
11279
11280     if (game.centered_player_nr_next == -1)
11281     {
11282       setScreenCenteredToAllPlayers(&sx, &sy);
11283     }
11284     else
11285     {
11286       sx = stored_player[game.centered_player_nr_next].jx;
11287       sy = stored_player[game.centered_player_nr_next].jy;
11288     }
11289
11290     game.centered_player_nr = game.centered_player_nr_next;
11291     game.set_centered_player = FALSE;
11292
11293     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11294     DrawGameDoorValues();
11295   }
11296
11297   for (i = 0; i < MAX_PLAYERS; i++)
11298   {
11299     int actual_player_action = stored_player[i].effective_action;
11300
11301 #if 1
11302     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11303        - rnd_equinox_tetrachloride 048
11304        - rnd_equinox_tetrachloride_ii 096
11305        - rnd_emanuel_schmieg 002
11306        - doctor_sloan_ww 001, 020
11307     */
11308     if (stored_player[i].MovPos == 0)
11309       CheckGravityMovement(&stored_player[i]);
11310 #endif
11311
11312     /* overwrite programmed action with tape action */
11313     if (stored_player[i].programmed_action)
11314       actual_player_action = stored_player[i].programmed_action;
11315
11316     PlayerActions(&stored_player[i], actual_player_action);
11317
11318     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11319   }
11320
11321   ScrollScreen(NULL, SCROLL_GO_ON);
11322
11323   /* for backwards compatibility, the following code emulates a fixed bug that
11324      occured when pushing elements (causing elements that just made their last
11325      pushing step to already (if possible) make their first falling step in the
11326      same game frame, which is bad); this code is also needed to use the famous
11327      "spring push bug" which is used in older levels and might be wanted to be
11328      used also in newer levels, but in this case the buggy pushing code is only
11329      affecting the "spring" element and no other elements */
11330
11331   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11332   {
11333     for (i = 0; i < MAX_PLAYERS; i++)
11334     {
11335       struct PlayerInfo *player = &stored_player[i];
11336       int x = player->jx;
11337       int y = player->jy;
11338
11339       if (player->active && player->is_pushing && player->is_moving &&
11340           IS_MOVING(x, y) &&
11341           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11342            Feld[x][y] == EL_SPRING))
11343       {
11344         ContinueMoving(x, y);
11345
11346         /* continue moving after pushing (this is actually a bug) */
11347         if (!IS_MOVING(x, y))
11348           Stop[x][y] = FALSE;
11349       }
11350     }
11351   }
11352
11353   SCAN_PLAYFIELD(x, y)
11354   {
11355     ChangeCount[x][y] = 0;
11356     ChangeEvent[x][y] = -1;
11357
11358     /* this must be handled before main playfield loop */
11359     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11360     {
11361       MovDelay[x][y]--;
11362       if (MovDelay[x][y] <= 0)
11363         RemoveField(x, y);
11364     }
11365
11366     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11367     {
11368       MovDelay[x][y]--;
11369       if (MovDelay[x][y] <= 0)
11370       {
11371         RemoveField(x, y);
11372         TEST_DrawLevelField(x, y);
11373
11374         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11375       }
11376     }
11377
11378 #if DEBUG
11379     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11380     {
11381       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11382       printf("GameActions(): This should never happen!\n");
11383
11384       ChangePage[x][y] = -1;
11385     }
11386 #endif
11387
11388     Stop[x][y] = FALSE;
11389     if (WasJustMoving[x][y] > 0)
11390       WasJustMoving[x][y]--;
11391     if (WasJustFalling[x][y] > 0)
11392       WasJustFalling[x][y]--;
11393     if (CheckCollision[x][y] > 0)
11394       CheckCollision[x][y]--;
11395     if (CheckImpact[x][y] > 0)
11396       CheckImpact[x][y]--;
11397
11398     GfxFrame[x][y]++;
11399
11400     /* reset finished pushing action (not done in ContinueMoving() to allow
11401        continuous pushing animation for elements with zero push delay) */
11402     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11403     {
11404       ResetGfxAnimation(x, y);
11405       TEST_DrawLevelField(x, y);
11406     }
11407
11408 #if DEBUG
11409     if (IS_BLOCKED(x, y))
11410     {
11411       int oldx, oldy;
11412
11413       Blocked2Moving(x, y, &oldx, &oldy);
11414       if (!IS_MOVING(oldx, oldy))
11415       {
11416         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11417         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11418         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11419         printf("GameActions(): This should never happen!\n");
11420       }
11421     }
11422 #endif
11423   }
11424
11425   SCAN_PLAYFIELD(x, y)
11426   {
11427     element = Feld[x][y];
11428     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11429
11430     ResetGfxFrame(x, y, TRUE);
11431
11432     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11433         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11434       ResetRandomAnimationValue(x, y);
11435
11436     SetRandomAnimationValue(x, y);
11437
11438     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11439
11440     if (IS_INACTIVE(element))
11441     {
11442       if (IS_ANIMATED(graphic))
11443         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11444
11445       continue;
11446     }
11447
11448     /* this may take place after moving, so 'element' may have changed */
11449     if (IS_CHANGING(x, y) &&
11450         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11451     {
11452       int page = element_info[element].event_page_nr[CE_DELAY];
11453
11454       HandleElementChange(x, y, page);
11455
11456       element = Feld[x][y];
11457       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11458     }
11459
11460     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11461     {
11462       StartMoving(x, y);
11463
11464       element = Feld[x][y];
11465       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11466
11467       if (IS_ANIMATED(graphic) &&
11468           !IS_MOVING(x, y) &&
11469           !Stop[x][y])
11470         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11471
11472       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11473         TEST_DrawTwinkleOnField(x, y);
11474     }
11475     else if ((element == EL_ACID ||
11476               element == EL_EXIT_OPEN ||
11477               element == EL_EM_EXIT_OPEN ||
11478               element == EL_SP_EXIT_OPEN ||
11479               element == EL_STEEL_EXIT_OPEN ||
11480               element == EL_EM_STEEL_EXIT_OPEN ||
11481               element == EL_SP_TERMINAL ||
11482               element == EL_SP_TERMINAL_ACTIVE ||
11483               element == EL_EXTRA_TIME ||
11484               element == EL_SHIELD_NORMAL ||
11485               element == EL_SHIELD_DEADLY) &&
11486              IS_ANIMATED(graphic))
11487       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11488     else if (IS_MOVING(x, y))
11489       ContinueMoving(x, y);
11490     else if (IS_ACTIVE_BOMB(element))
11491       CheckDynamite(x, y);
11492     else if (element == EL_AMOEBA_GROWING)
11493       AmoebeWaechst(x, y);
11494     else if (element == EL_AMOEBA_SHRINKING)
11495       AmoebaDisappearing(x, y);
11496
11497 #if !USE_NEW_AMOEBA_CODE
11498     else if (IS_AMOEBALIVE(element))
11499       AmoebeAbleger(x, y);
11500 #endif
11501
11502     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11503       Life(x, y);
11504     else if (element == EL_EXIT_CLOSED)
11505       CheckExit(x, y);
11506     else if (element == EL_EM_EXIT_CLOSED)
11507       CheckExitEM(x, y);
11508     else if (element == EL_STEEL_EXIT_CLOSED)
11509       CheckExitSteel(x, y);
11510     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11511       CheckExitSteelEM(x, y);
11512     else if (element == EL_SP_EXIT_CLOSED)
11513       CheckExitSP(x, y);
11514     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11515              element == EL_EXPANDABLE_STEELWALL_GROWING)
11516       MauerWaechst(x, y);
11517     else if (element == EL_EXPANDABLE_WALL ||
11518              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11519              element == EL_EXPANDABLE_WALL_VERTICAL ||
11520              element == EL_EXPANDABLE_WALL_ANY ||
11521              element == EL_BD_EXPANDABLE_WALL)
11522       MauerAbleger(x, y);
11523     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11524              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11525              element == EL_EXPANDABLE_STEELWALL_ANY)
11526       MauerAblegerStahl(x, y);
11527     else if (element == EL_FLAMES)
11528       CheckForDragon(x, y);
11529     else if (element == EL_EXPLOSION)
11530       ; /* drawing of correct explosion animation is handled separately */
11531     else if (element == EL_ELEMENT_SNAPPING ||
11532              element == EL_DIAGONAL_SHRINKING ||
11533              element == EL_DIAGONAL_GROWING)
11534     {
11535       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11536
11537       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11538     }
11539     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11540       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11541
11542     if (IS_BELT_ACTIVE(element))
11543       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11544
11545     if (game.magic_wall_active)
11546     {
11547       int jx = local_player->jx, jy = local_player->jy;
11548
11549       /* play the element sound at the position nearest to the player */
11550       if ((element == EL_MAGIC_WALL_FULL ||
11551            element == EL_MAGIC_WALL_ACTIVE ||
11552            element == EL_MAGIC_WALL_EMPTYING ||
11553            element == EL_BD_MAGIC_WALL_FULL ||
11554            element == EL_BD_MAGIC_WALL_ACTIVE ||
11555            element == EL_BD_MAGIC_WALL_EMPTYING ||
11556            element == EL_DC_MAGIC_WALL_FULL ||
11557            element == EL_DC_MAGIC_WALL_ACTIVE ||
11558            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11559           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11560       {
11561         magic_wall_x = x;
11562         magic_wall_y = y;
11563       }
11564     }
11565   }
11566
11567 #if USE_NEW_AMOEBA_CODE
11568   /* new experimental amoeba growth stuff */
11569   if (!(FrameCounter % 8))
11570   {
11571     static unsigned int random = 1684108901;
11572
11573     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11574     {
11575       x = RND(lev_fieldx);
11576       y = RND(lev_fieldy);
11577       element = Feld[x][y];
11578
11579       if (!IS_PLAYER(x,y) &&
11580           (element == EL_EMPTY ||
11581            CAN_GROW_INTO(element) ||
11582            element == EL_QUICKSAND_EMPTY ||
11583            element == EL_QUICKSAND_FAST_EMPTY ||
11584            element == EL_ACID_SPLASH_LEFT ||
11585            element == EL_ACID_SPLASH_RIGHT))
11586       {
11587         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11588             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11589             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11590             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11591           Feld[x][y] = EL_AMOEBA_DROP;
11592       }
11593
11594       random = random * 129 + 1;
11595     }
11596   }
11597 #endif
11598
11599   game.explosions_delayed = FALSE;
11600
11601   SCAN_PLAYFIELD(x, y)
11602   {
11603     element = Feld[x][y];
11604
11605     if (ExplodeField[x][y])
11606       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11607     else if (element == EL_EXPLOSION)
11608       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11609
11610     ExplodeField[x][y] = EX_TYPE_NONE;
11611   }
11612
11613   game.explosions_delayed = TRUE;
11614
11615   if (game.magic_wall_active)
11616   {
11617     if (!(game.magic_wall_time_left % 4))
11618     {
11619       int element = Feld[magic_wall_x][magic_wall_y];
11620
11621       if (element == EL_BD_MAGIC_WALL_FULL ||
11622           element == EL_BD_MAGIC_WALL_ACTIVE ||
11623           element == EL_BD_MAGIC_WALL_EMPTYING)
11624         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11625       else if (element == EL_DC_MAGIC_WALL_FULL ||
11626                element == EL_DC_MAGIC_WALL_ACTIVE ||
11627                element == EL_DC_MAGIC_WALL_EMPTYING)
11628         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11629       else
11630         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11631     }
11632
11633     if (game.magic_wall_time_left > 0)
11634     {
11635       game.magic_wall_time_left--;
11636
11637       if (!game.magic_wall_time_left)
11638       {
11639         SCAN_PLAYFIELD(x, y)
11640         {
11641           element = Feld[x][y];
11642
11643           if (element == EL_MAGIC_WALL_ACTIVE ||
11644               element == EL_MAGIC_WALL_FULL)
11645           {
11646             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11647             TEST_DrawLevelField(x, y);
11648           }
11649           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11650                    element == EL_BD_MAGIC_WALL_FULL)
11651           {
11652             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11653             TEST_DrawLevelField(x, y);
11654           }
11655           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11656                    element == EL_DC_MAGIC_WALL_FULL)
11657           {
11658             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11659             TEST_DrawLevelField(x, y);
11660           }
11661         }
11662
11663         game.magic_wall_active = FALSE;
11664       }
11665     }
11666   }
11667
11668   if (game.light_time_left > 0)
11669   {
11670     game.light_time_left--;
11671
11672     if (game.light_time_left == 0)
11673       RedrawAllLightSwitchesAndInvisibleElements();
11674   }
11675
11676   if (game.timegate_time_left > 0)
11677   {
11678     game.timegate_time_left--;
11679
11680     if (game.timegate_time_left == 0)
11681       CloseAllOpenTimegates();
11682   }
11683
11684   if (game.lenses_time_left > 0)
11685   {
11686     game.lenses_time_left--;
11687
11688     if (game.lenses_time_left == 0)
11689       RedrawAllInvisibleElementsForLenses();
11690   }
11691
11692   if (game.magnify_time_left > 0)
11693   {
11694     game.magnify_time_left--;
11695
11696     if (game.magnify_time_left == 0)
11697       RedrawAllInvisibleElementsForMagnifier();
11698   }
11699
11700   for (i = 0; i < MAX_PLAYERS; i++)
11701   {
11702     struct PlayerInfo *player = &stored_player[i];
11703
11704     if (SHIELD_ON(player))
11705     {
11706       if (player->shield_deadly_time_left)
11707         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11708       else if (player->shield_normal_time_left)
11709         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11710     }
11711   }
11712
11713 #if USE_DELAYED_GFX_REDRAW
11714   SCAN_PLAYFIELD(x, y)
11715   {
11716     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11717     {
11718       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11719          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11720
11721       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11722         DrawLevelField(x, y);
11723
11724       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11725         DrawLevelFieldCrumbled(x, y);
11726
11727       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11728         DrawLevelFieldCrumbledNeighbours(x, y);
11729
11730       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11731         DrawTwinkleOnField(x, y);
11732     }
11733
11734     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11735   }
11736 #endif
11737
11738   CheckLevelTime();
11739
11740   DrawAllPlayers();
11741   PlayAllPlayersSound();
11742
11743   if (options.debug)                    /* calculate frames per second */
11744   {
11745     static unsigned int fps_counter = 0;
11746     static int fps_frames = 0;
11747     unsigned int fps_delay_ms = Counter() - fps_counter;
11748
11749     fps_frames++;
11750
11751     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11752     {
11753       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11754
11755       fps_frames = 0;
11756       fps_counter = Counter();
11757     }
11758
11759     redraw_mask |= REDRAW_FPS;
11760   }
11761
11762   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11763
11764   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11765   {
11766     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11767
11768     local_player->show_envelope = 0;
11769   }
11770
11771   /* use random number generator in every frame to make it less predictable */
11772   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11773     RND(1);
11774 }
11775
11776 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11777 {
11778   int min_x = x, min_y = y, max_x = x, max_y = y;
11779   int i;
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782   {
11783     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11784
11785     if (!stored_player[i].active || &stored_player[i] == player)
11786       continue;
11787
11788     min_x = MIN(min_x, jx);
11789     min_y = MIN(min_y, jy);
11790     max_x = MAX(max_x, jx);
11791     max_y = MAX(max_y, jy);
11792   }
11793
11794   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11795 }
11796
11797 static boolean AllPlayersInVisibleScreen()
11798 {
11799   int i;
11800
11801   for (i = 0; i < MAX_PLAYERS; i++)
11802   {
11803     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11804
11805     if (!stored_player[i].active)
11806       continue;
11807
11808     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11809       return FALSE;
11810   }
11811
11812   return TRUE;
11813 }
11814
11815 void ScrollLevel(int dx, int dy)
11816 {
11817   int scroll_offset = 2 * TILEX_VAR;
11818   int x, y;
11819
11820   BlitBitmap(drawto_field, drawto_field,
11821              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11822              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11823              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11824              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11825              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11826              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11827
11828   if (dx != 0)
11829   {
11830     x = (dx == 1 ? BX1 : BX2);
11831     for (y = BY1; y <= BY2; y++)
11832       DrawScreenField(x, y);
11833   }
11834
11835   if (dy != 0)
11836   {
11837     y = (dy == 1 ? BY1 : BY2);
11838     for (x = BX1; x <= BX2; x++)
11839       DrawScreenField(x, y);
11840   }
11841
11842   redraw_mask |= REDRAW_FIELD;
11843 }
11844
11845 static boolean canFallDown(struct PlayerInfo *player)
11846 {
11847   int jx = player->jx, jy = player->jy;
11848
11849   return (IN_LEV_FIELD(jx, jy + 1) &&
11850           (IS_FREE(jx, jy + 1) ||
11851            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11852           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11853           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11854 }
11855
11856 static boolean canPassField(int x, int y, int move_dir)
11857 {
11858   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11859   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11860   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11861   int nextx = x + dx;
11862   int nexty = y + dy;
11863   int element = Feld[x][y];
11864
11865   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11866           !CAN_MOVE(element) &&
11867           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11868           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11869           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11870 }
11871
11872 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11873 {
11874   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11875   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11876   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11877   int newx = x + dx;
11878   int newy = y + dy;
11879
11880   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11881           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11882           (IS_DIGGABLE(Feld[newx][newy]) ||
11883            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11884            canPassField(newx, newy, move_dir)));
11885 }
11886
11887 static void CheckGravityMovement(struct PlayerInfo *player)
11888 {
11889   if (player->gravity && !player->programmed_action)
11890   {
11891     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11892     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11893     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11894     int jx = player->jx, jy = player->jy;
11895     boolean player_is_moving_to_valid_field =
11896       (!player_is_snapping &&
11897        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11898         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11899     boolean player_can_fall_down = canFallDown(player);
11900
11901     if (player_can_fall_down &&
11902         !player_is_moving_to_valid_field)
11903       player->programmed_action = MV_DOWN;
11904   }
11905 }
11906
11907 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11908 {
11909   return CheckGravityMovement(player);
11910
11911   if (player->gravity && !player->programmed_action)
11912   {
11913     int jx = player->jx, jy = player->jy;
11914     boolean field_under_player_is_free =
11915       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11916     boolean player_is_standing_on_valid_field =
11917       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11918        (IS_WALKABLE(Feld[jx][jy]) &&
11919         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11920
11921     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11922       player->programmed_action = MV_DOWN;
11923   }
11924 }
11925
11926 /*
11927   MovePlayerOneStep()
11928   -----------------------------------------------------------------------------
11929   dx, dy:               direction (non-diagonal) to try to move the player to
11930   real_dx, real_dy:     direction as read from input device (can be diagonal)
11931 */
11932
11933 boolean MovePlayerOneStep(struct PlayerInfo *player,
11934                           int dx, int dy, int real_dx, int real_dy)
11935 {
11936   int jx = player->jx, jy = player->jy;
11937   int new_jx = jx + dx, new_jy = jy + dy;
11938   int can_move;
11939   boolean player_can_move = !player->cannot_move;
11940
11941   if (!player->active || (!dx && !dy))
11942     return MP_NO_ACTION;
11943
11944   player->MovDir = (dx < 0 ? MV_LEFT :
11945                     dx > 0 ? MV_RIGHT :
11946                     dy < 0 ? MV_UP :
11947                     dy > 0 ? MV_DOWN :  MV_NONE);
11948
11949   if (!IN_LEV_FIELD(new_jx, new_jy))
11950     return MP_NO_ACTION;
11951
11952   if (!player_can_move)
11953   {
11954     if (player->MovPos == 0)
11955     {
11956       player->is_moving = FALSE;
11957       player->is_digging = FALSE;
11958       player->is_collecting = FALSE;
11959       player->is_snapping = FALSE;
11960       player->is_pushing = FALSE;
11961     }
11962   }
11963
11964   if (!options.network && game.centered_player_nr == -1 &&
11965       !AllPlayersInSight(player, new_jx, new_jy))
11966     return MP_NO_ACTION;
11967
11968   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11969   if (can_move != MP_MOVING)
11970     return can_move;
11971
11972   /* check if DigField() has caused relocation of the player */
11973   if (player->jx != jx || player->jy != jy)
11974     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11975
11976   StorePlayer[jx][jy] = 0;
11977   player->last_jx = jx;
11978   player->last_jy = jy;
11979   player->jx = new_jx;
11980   player->jy = new_jy;
11981   StorePlayer[new_jx][new_jy] = player->element_nr;
11982
11983   if (player->move_delay_value_next != -1)
11984   {
11985     player->move_delay_value = player->move_delay_value_next;
11986     player->move_delay_value_next = -1;
11987   }
11988
11989   player->MovPos =
11990     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11991
11992   player->step_counter++;
11993
11994   PlayerVisit[jx][jy] = FrameCounter;
11995
11996   player->is_moving = TRUE;
11997
11998 #if 1
11999   /* should better be called in MovePlayer(), but this breaks some tapes */
12000   ScrollPlayer(player, SCROLL_INIT);
12001 #endif
12002
12003   return MP_MOVING;
12004 }
12005
12006 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12007 {
12008   int jx = player->jx, jy = player->jy;
12009   int old_jx = jx, old_jy = jy;
12010   int moved = MP_NO_ACTION;
12011
12012   if (!player->active)
12013     return FALSE;
12014
12015   if (!dx && !dy)
12016   {
12017     if (player->MovPos == 0)
12018     {
12019       player->is_moving = FALSE;
12020       player->is_digging = FALSE;
12021       player->is_collecting = FALSE;
12022       player->is_snapping = FALSE;
12023       player->is_pushing = FALSE;
12024     }
12025
12026     return FALSE;
12027   }
12028
12029   if (player->move_delay > 0)
12030     return FALSE;
12031
12032   player->move_delay = -1;              /* set to "uninitialized" value */
12033
12034   /* store if player is automatically moved to next field */
12035   player->is_auto_moving = (player->programmed_action != MV_NONE);
12036
12037   /* remove the last programmed player action */
12038   player->programmed_action = 0;
12039
12040   if (player->MovPos)
12041   {
12042     /* should only happen if pre-1.2 tape recordings are played */
12043     /* this is only for backward compatibility */
12044
12045     int original_move_delay_value = player->move_delay_value;
12046
12047 #if DEBUG
12048     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12049            tape.counter);
12050 #endif
12051
12052     /* scroll remaining steps with finest movement resolution */
12053     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12054
12055     while (player->MovPos)
12056     {
12057       ScrollPlayer(player, SCROLL_GO_ON);
12058       ScrollScreen(NULL, SCROLL_GO_ON);
12059
12060       AdvanceFrameAndPlayerCounters(player->index_nr);
12061
12062       DrawAllPlayers();
12063       BackToFront();
12064     }
12065
12066     player->move_delay_value = original_move_delay_value;
12067   }
12068
12069   player->is_active = FALSE;
12070
12071   if (player->last_move_dir & MV_HORIZONTAL)
12072   {
12073     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12074       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12075   }
12076   else
12077   {
12078     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12079       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12080   }
12081
12082   if (!moved && !player->is_active)
12083   {
12084     player->is_moving = FALSE;
12085     player->is_digging = FALSE;
12086     player->is_collecting = FALSE;
12087     player->is_snapping = FALSE;
12088     player->is_pushing = FALSE;
12089   }
12090
12091   jx = player->jx;
12092   jy = player->jy;
12093
12094   if (moved & MP_MOVING && !ScreenMovPos &&
12095       (player->index_nr == game.centered_player_nr ||
12096        game.centered_player_nr == -1))
12097   {
12098     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12099     int offset = game.scroll_delay_value;
12100
12101     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12102     {
12103       /* actual player has left the screen -- scroll in that direction */
12104       if (jx != old_jx)         /* player has moved horizontally */
12105         scroll_x += (jx - old_jx);
12106       else                      /* player has moved vertically */
12107         scroll_y += (jy - old_jy);
12108     }
12109     else
12110     {
12111       if (jx != old_jx)         /* player has moved horizontally */
12112       {
12113         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12114             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12115           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12116
12117         /* don't scroll over playfield boundaries */
12118         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12119           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12120
12121         /* don't scroll more than one field at a time */
12122         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12123
12124         /* don't scroll against the player's moving direction */
12125         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12126             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12127           scroll_x = old_scroll_x;
12128       }
12129       else                      /* player has moved vertically */
12130       {
12131         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12132             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12133           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12134
12135         /* don't scroll over playfield boundaries */
12136         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12137           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12138
12139         /* don't scroll more than one field at a time */
12140         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12141
12142         /* don't scroll against the player's moving direction */
12143         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12144             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12145           scroll_y = old_scroll_y;
12146       }
12147     }
12148
12149     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12150     {
12151       if (!options.network && game.centered_player_nr == -1 &&
12152           !AllPlayersInVisibleScreen())
12153       {
12154         scroll_x = old_scroll_x;
12155         scroll_y = old_scroll_y;
12156       }
12157       else
12158       {
12159         ScrollScreen(player, SCROLL_INIT);
12160         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12161       }
12162     }
12163   }
12164
12165   player->StepFrame = 0;
12166
12167   if (moved & MP_MOVING)
12168   {
12169     if (old_jx != jx && old_jy == jy)
12170       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12171     else if (old_jx == jx && old_jy != jy)
12172       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12173
12174     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12175
12176     player->last_move_dir = player->MovDir;
12177     player->is_moving = TRUE;
12178     player->is_snapping = FALSE;
12179     player->is_switching = FALSE;
12180     player->is_dropping = FALSE;
12181     player->is_dropping_pressed = FALSE;
12182     player->drop_pressed_delay = 0;
12183
12184 #if 0
12185     /* should better be called here than above, but this breaks some tapes */
12186     ScrollPlayer(player, SCROLL_INIT);
12187 #endif
12188   }
12189   else
12190   {
12191     CheckGravityMovementWhenNotMoving(player);
12192
12193     player->is_moving = FALSE;
12194
12195     /* at this point, the player is allowed to move, but cannot move right now
12196        (e.g. because of something blocking the way) -- ensure that the player
12197        is also allowed to move in the next frame (in old versions before 3.1.1,
12198        the player was forced to wait again for eight frames before next try) */
12199
12200     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12201       player->move_delay = 0;   /* allow direct movement in the next frame */
12202   }
12203
12204   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12205     player->move_delay = player->move_delay_value;
12206
12207   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12208   {
12209     TestIfPlayerTouchesBadThing(jx, jy);
12210     TestIfPlayerTouchesCustomElement(jx, jy);
12211   }
12212
12213   if (!player->active)
12214     RemovePlayer(player);
12215
12216   return moved;
12217 }
12218
12219 void ScrollPlayer(struct PlayerInfo *player, int mode)
12220 {
12221   int jx = player->jx, jy = player->jy;
12222   int last_jx = player->last_jx, last_jy = player->last_jy;
12223   int move_stepsize = TILEX / player->move_delay_value;
12224
12225   if (!player->active)
12226     return;
12227
12228   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12229     return;
12230
12231   if (mode == SCROLL_INIT)
12232   {
12233     player->actual_frame_counter = FrameCounter;
12234     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12235
12236     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12237         Feld[last_jx][last_jy] == EL_EMPTY)
12238     {
12239       int last_field_block_delay = 0;   /* start with no blocking at all */
12240       int block_delay_adjustment = player->block_delay_adjustment;
12241
12242       /* if player blocks last field, add delay for exactly one move */
12243       if (player->block_last_field)
12244       {
12245         last_field_block_delay += player->move_delay_value;
12246
12247         /* when blocking enabled, prevent moving up despite gravity */
12248         if (player->gravity && player->MovDir == MV_UP)
12249           block_delay_adjustment = -1;
12250       }
12251
12252       /* add block delay adjustment (also possible when not blocking) */
12253       last_field_block_delay += block_delay_adjustment;
12254
12255       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12256       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12257     }
12258
12259     if (player->MovPos != 0)    /* player has not yet reached destination */
12260       return;
12261   }
12262   else if (!FrameReached(&player->actual_frame_counter, 1))
12263     return;
12264
12265   if (player->MovPos != 0)
12266   {
12267     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12268     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12269
12270     /* before DrawPlayer() to draw correct player graphic for this case */
12271     if (player->MovPos == 0)
12272       CheckGravityMovement(player);
12273   }
12274
12275   if (player->MovPos == 0)      /* player reached destination field */
12276   {
12277     if (player->move_delay_reset_counter > 0)
12278     {
12279       player->move_delay_reset_counter--;
12280
12281       if (player->move_delay_reset_counter == 0)
12282       {
12283         /* continue with normal speed after quickly moving through gate */
12284         HALVE_PLAYER_SPEED(player);
12285
12286         /* be able to make the next move without delay */
12287         player->move_delay = 0;
12288       }
12289     }
12290
12291     player->last_jx = jx;
12292     player->last_jy = jy;
12293
12294     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12295         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12296         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12297         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12298         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12299         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12300         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12301         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12302     {
12303       DrawPlayer(player);       /* needed here only to cleanup last field */
12304       RemovePlayer(player);
12305
12306       if (local_player->friends_still_needed == 0 ||
12307           IS_SP_ELEMENT(Feld[jx][jy]))
12308         PlayerWins(player);
12309     }
12310
12311     /* this breaks one level: "machine", level 000 */
12312     {
12313       int move_direction = player->MovDir;
12314       int enter_side = MV_DIR_OPPOSITE(move_direction);
12315       int leave_side = move_direction;
12316       int old_jx = last_jx;
12317       int old_jy = last_jy;
12318       int old_element = Feld[old_jx][old_jy];
12319       int new_element = Feld[jx][jy];
12320
12321       if (IS_CUSTOM_ELEMENT(old_element))
12322         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12323                                    CE_LEFT_BY_PLAYER,
12324                                    player->index_bit, leave_side);
12325
12326       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12327                                           CE_PLAYER_LEAVES_X,
12328                                           player->index_bit, leave_side);
12329
12330       if (IS_CUSTOM_ELEMENT(new_element))
12331         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12332                                    player->index_bit, enter_side);
12333
12334       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12335                                           CE_PLAYER_ENTERS_X,
12336                                           player->index_bit, enter_side);
12337
12338       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12339                                         CE_MOVE_OF_X, move_direction);
12340     }
12341
12342     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12343     {
12344       TestIfPlayerTouchesBadThing(jx, jy);
12345       TestIfPlayerTouchesCustomElement(jx, jy);
12346
12347       /* needed because pushed element has not yet reached its destination,
12348          so it would trigger a change event at its previous field location */
12349       if (!player->is_pushing)
12350         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12351
12352       if (!player->active)
12353         RemovePlayer(player);
12354     }
12355
12356     if (!local_player->LevelSolved && level.use_step_counter)
12357     {
12358       int i;
12359
12360       TimePlayed++;
12361
12362       if (TimeLeft > 0)
12363       {
12364         TimeLeft--;
12365
12366         if (TimeLeft <= 10 && setup.time_limit)
12367           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12368
12369         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12370
12371         DisplayGameControlValues();
12372
12373         if (!TimeLeft && setup.time_limit)
12374           for (i = 0; i < MAX_PLAYERS; i++)
12375             KillPlayer(&stored_player[i]);
12376       }
12377       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12378       {
12379         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12380
12381         DisplayGameControlValues();
12382       }
12383     }
12384
12385     if (tape.single_step && tape.recording && !tape.pausing &&
12386         !player->programmed_action)
12387       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12388
12389     if (!player->programmed_action)
12390       CheckSaveEngineSnapshot(player);
12391   }
12392 }
12393
12394 void ScrollScreen(struct PlayerInfo *player, int mode)
12395 {
12396   static unsigned int screen_frame_counter = 0;
12397
12398   if (mode == SCROLL_INIT)
12399   {
12400     /* set scrolling step size according to actual player's moving speed */
12401     ScrollStepSize = TILEX / player->move_delay_value;
12402
12403     screen_frame_counter = FrameCounter;
12404     ScreenMovDir = player->MovDir;
12405     ScreenMovPos = player->MovPos;
12406     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12407     return;
12408   }
12409   else if (!FrameReached(&screen_frame_counter, 1))
12410     return;
12411
12412   if (ScreenMovPos)
12413   {
12414     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12415     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12416     redraw_mask |= REDRAW_FIELD;
12417   }
12418   else
12419     ScreenMovDir = MV_NONE;
12420 }
12421
12422 void TestIfPlayerTouchesCustomElement(int x, int y)
12423 {
12424   static int xy[4][2] =
12425   {
12426     { 0, -1 },
12427     { -1, 0 },
12428     { +1, 0 },
12429     { 0, +1 }
12430   };
12431   static int trigger_sides[4][2] =
12432   {
12433     /* center side       border side */
12434     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12435     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12436     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12437     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12438   };
12439   static int touch_dir[4] =
12440   {
12441     MV_LEFT | MV_RIGHT,
12442     MV_UP   | MV_DOWN,
12443     MV_UP   | MV_DOWN,
12444     MV_LEFT | MV_RIGHT
12445   };
12446   int center_element = Feld[x][y];      /* should always be non-moving! */
12447   int i;
12448
12449   for (i = 0; i < NUM_DIRECTIONS; i++)
12450   {
12451     int xx = x + xy[i][0];
12452     int yy = y + xy[i][1];
12453     int center_side = trigger_sides[i][0];
12454     int border_side = trigger_sides[i][1];
12455     int border_element;
12456
12457     if (!IN_LEV_FIELD(xx, yy))
12458       continue;
12459
12460     if (IS_PLAYER(x, y))                /* player found at center element */
12461     {
12462       struct PlayerInfo *player = PLAYERINFO(x, y);
12463
12464       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12465         border_element = Feld[xx][yy];          /* may be moving! */
12466       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12467         border_element = Feld[xx][yy];
12468       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12469         border_element = MovingOrBlocked2Element(xx, yy);
12470       else
12471         continue;               /* center and border element do not touch */
12472
12473       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12474                                  player->index_bit, border_side);
12475       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12476                                           CE_PLAYER_TOUCHES_X,
12477                                           player->index_bit, border_side);
12478
12479       {
12480         /* use player element that is initially defined in the level playfield,
12481            not the player element that corresponds to the runtime player number
12482            (example: a level that contains EL_PLAYER_3 as the only player would
12483            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12484         int player_element = PLAYERINFO(x, y)->initial_element;
12485
12486         CheckElementChangeBySide(xx, yy, border_element, player_element,
12487                                  CE_TOUCHING_X, border_side);
12488       }
12489     }
12490     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12491     {
12492       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12493
12494       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12495       {
12496         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12497           continue;             /* center and border element do not touch */
12498       }
12499
12500       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12501                                  player->index_bit, center_side);
12502       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12503                                           CE_PLAYER_TOUCHES_X,
12504                                           player->index_bit, center_side);
12505
12506       {
12507         /* use player element that is initially defined in the level playfield,
12508            not the player element that corresponds to the runtime player number
12509            (example: a level that contains EL_PLAYER_3 as the only player would
12510            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12511         int player_element = PLAYERINFO(xx, yy)->initial_element;
12512
12513         CheckElementChangeBySide(x, y, center_element, player_element,
12514                                  CE_TOUCHING_X, center_side);
12515       }
12516
12517       break;
12518     }
12519   }
12520 }
12521
12522 void TestIfElementTouchesCustomElement(int x, int y)
12523 {
12524   static int xy[4][2] =
12525   {
12526     { 0, -1 },
12527     { -1, 0 },
12528     { +1, 0 },
12529     { 0, +1 }
12530   };
12531   static int trigger_sides[4][2] =
12532   {
12533     /* center side      border side */
12534     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12535     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12536     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12537     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12538   };
12539   static int touch_dir[4] =
12540   {
12541     MV_LEFT | MV_RIGHT,
12542     MV_UP   | MV_DOWN,
12543     MV_UP   | MV_DOWN,
12544     MV_LEFT | MV_RIGHT
12545   };
12546   boolean change_center_element = FALSE;
12547   int center_element = Feld[x][y];      /* should always be non-moving! */
12548   int border_element_old[NUM_DIRECTIONS];
12549   int i;
12550
12551   for (i = 0; i < NUM_DIRECTIONS; i++)
12552   {
12553     int xx = x + xy[i][0];
12554     int yy = y + xy[i][1];
12555     int border_element;
12556
12557     border_element_old[i] = -1;
12558
12559     if (!IN_LEV_FIELD(xx, yy))
12560       continue;
12561
12562     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12563       border_element = Feld[xx][yy];    /* may be moving! */
12564     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12565       border_element = Feld[xx][yy];
12566     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12567       border_element = MovingOrBlocked2Element(xx, yy);
12568     else
12569       continue;                 /* center and border element do not touch */
12570
12571     border_element_old[i] = border_element;
12572   }
12573
12574   for (i = 0; i < NUM_DIRECTIONS; i++)
12575   {
12576     int xx = x + xy[i][0];
12577     int yy = y + xy[i][1];
12578     int center_side = trigger_sides[i][0];
12579     int border_element = border_element_old[i];
12580
12581     if (border_element == -1)
12582       continue;
12583
12584     /* check for change of border element */
12585     CheckElementChangeBySide(xx, yy, border_element, center_element,
12586                              CE_TOUCHING_X, center_side);
12587
12588     /* (center element cannot be player, so we dont have to check this here) */
12589   }
12590
12591   for (i = 0; i < NUM_DIRECTIONS; i++)
12592   {
12593     int xx = x + xy[i][0];
12594     int yy = y + xy[i][1];
12595     int border_side = trigger_sides[i][1];
12596     int border_element = border_element_old[i];
12597
12598     if (border_element == -1)
12599       continue;
12600
12601     /* check for change of center element (but change it only once) */
12602     if (!change_center_element)
12603       change_center_element =
12604         CheckElementChangeBySide(x, y, center_element, border_element,
12605                                  CE_TOUCHING_X, border_side);
12606
12607     if (IS_PLAYER(xx, yy))
12608     {
12609       /* use player element that is initially defined in the level playfield,
12610          not the player element that corresponds to the runtime player number
12611          (example: a level that contains EL_PLAYER_3 as the only player would
12612          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12613       int player_element = PLAYERINFO(xx, yy)->initial_element;
12614
12615       CheckElementChangeBySide(x, y, center_element, player_element,
12616                                CE_TOUCHING_X, border_side);
12617     }
12618   }
12619 }
12620
12621 void TestIfElementHitsCustomElement(int x, int y, int direction)
12622 {
12623   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12624   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12625   int hitx = x + dx, hity = y + dy;
12626   int hitting_element = Feld[x][y];
12627   int touched_element;
12628
12629   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12630     return;
12631
12632   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12633                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12634
12635   if (IN_LEV_FIELD(hitx, hity))
12636   {
12637     int opposite_direction = MV_DIR_OPPOSITE(direction);
12638     int hitting_side = direction;
12639     int touched_side = opposite_direction;
12640     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12641                           MovDir[hitx][hity] != direction ||
12642                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12643
12644     object_hit = TRUE;
12645
12646     if (object_hit)
12647     {
12648       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12649                                CE_HITTING_X, touched_side);
12650
12651       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12652                                CE_HIT_BY_X, hitting_side);
12653
12654       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12655                                CE_HIT_BY_SOMETHING, opposite_direction);
12656
12657       if (IS_PLAYER(hitx, hity))
12658       {
12659         /* use player element that is initially defined in the level playfield,
12660            not the player element that corresponds to the runtime player number
12661            (example: a level that contains EL_PLAYER_3 as the only player would
12662            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12663         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12664
12665         CheckElementChangeBySide(x, y, hitting_element, player_element,
12666                                  CE_HITTING_X, touched_side);
12667       }
12668     }
12669   }
12670
12671   /* "hitting something" is also true when hitting the playfield border */
12672   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12673                            CE_HITTING_SOMETHING, direction);
12674 }
12675
12676 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12677 {
12678   int i, kill_x = -1, kill_y = -1;
12679
12680   int bad_element = -1;
12681   static int test_xy[4][2] =
12682   {
12683     { 0, -1 },
12684     { -1, 0 },
12685     { +1, 0 },
12686     { 0, +1 }
12687   };
12688   static int test_dir[4] =
12689   {
12690     MV_UP,
12691     MV_LEFT,
12692     MV_RIGHT,
12693     MV_DOWN
12694   };
12695
12696   for (i = 0; i < NUM_DIRECTIONS; i++)
12697   {
12698     int test_x, test_y, test_move_dir, test_element;
12699
12700     test_x = good_x + test_xy[i][0];
12701     test_y = good_y + test_xy[i][1];
12702
12703     if (!IN_LEV_FIELD(test_x, test_y))
12704       continue;
12705
12706     test_move_dir =
12707       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12708
12709     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12710
12711     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12712        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12713     */
12714     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12715         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12716     {
12717       kill_x = test_x;
12718       kill_y = test_y;
12719       bad_element = test_element;
12720
12721       break;
12722     }
12723   }
12724
12725   if (kill_x != -1 || kill_y != -1)
12726   {
12727     if (IS_PLAYER(good_x, good_y))
12728     {
12729       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12730
12731       if (player->shield_deadly_time_left > 0 &&
12732           !IS_INDESTRUCTIBLE(bad_element))
12733         Bang(kill_x, kill_y);
12734       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12735         KillPlayer(player);
12736     }
12737     else
12738       Bang(good_x, good_y);
12739   }
12740 }
12741
12742 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12743 {
12744   int i, kill_x = -1, kill_y = -1;
12745   int bad_element = Feld[bad_x][bad_y];
12746   static int test_xy[4][2] =
12747   {
12748     { 0, -1 },
12749     { -1, 0 },
12750     { +1, 0 },
12751     { 0, +1 }
12752   };
12753   static int touch_dir[4] =
12754   {
12755     MV_LEFT | MV_RIGHT,
12756     MV_UP   | MV_DOWN,
12757     MV_UP   | MV_DOWN,
12758     MV_LEFT | MV_RIGHT
12759   };
12760   static int test_dir[4] =
12761   {
12762     MV_UP,
12763     MV_LEFT,
12764     MV_RIGHT,
12765     MV_DOWN
12766   };
12767
12768   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12769     return;
12770
12771   for (i = 0; i < NUM_DIRECTIONS; i++)
12772   {
12773     int test_x, test_y, test_move_dir, test_element;
12774
12775     test_x = bad_x + test_xy[i][0];
12776     test_y = bad_y + test_xy[i][1];
12777
12778     if (!IN_LEV_FIELD(test_x, test_y))
12779       continue;
12780
12781     test_move_dir =
12782       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12783
12784     test_element = Feld[test_x][test_y];
12785
12786     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12787        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12788     */
12789     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12790         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12791     {
12792       /* good thing is player or penguin that does not move away */
12793       if (IS_PLAYER(test_x, test_y))
12794       {
12795         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12796
12797         if (bad_element == EL_ROBOT && player->is_moving)
12798           continue;     /* robot does not kill player if he is moving */
12799
12800         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12801         {
12802           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12803             continue;           /* center and border element do not touch */
12804         }
12805
12806         kill_x = test_x;
12807         kill_y = test_y;
12808
12809         break;
12810       }
12811       else if (test_element == EL_PENGUIN)
12812       {
12813         kill_x = test_x;
12814         kill_y = test_y;
12815
12816         break;
12817       }
12818     }
12819   }
12820
12821   if (kill_x != -1 || kill_y != -1)
12822   {
12823     if (IS_PLAYER(kill_x, kill_y))
12824     {
12825       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12826
12827       if (player->shield_deadly_time_left > 0 &&
12828           !IS_INDESTRUCTIBLE(bad_element))
12829         Bang(bad_x, bad_y);
12830       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12831         KillPlayer(player);
12832     }
12833     else
12834       Bang(kill_x, kill_y);
12835   }
12836 }
12837
12838 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12839 {
12840   int bad_element = Feld[bad_x][bad_y];
12841   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12842   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12843   int test_x = bad_x + dx, test_y = bad_y + dy;
12844   int test_move_dir, test_element;
12845   int kill_x = -1, kill_y = -1;
12846
12847   if (!IN_LEV_FIELD(test_x, test_y))
12848     return;
12849
12850   test_move_dir =
12851     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12852
12853   test_element = Feld[test_x][test_y];
12854
12855   if (test_move_dir != bad_move_dir)
12856   {
12857     /* good thing can be player or penguin that does not move away */
12858     if (IS_PLAYER(test_x, test_y))
12859     {
12860       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12861
12862       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12863          player as being hit when he is moving towards the bad thing, because
12864          the "get hit by" condition would be lost after the player stops) */
12865       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12866         return;         /* player moves away from bad thing */
12867
12868       kill_x = test_x;
12869       kill_y = test_y;
12870     }
12871     else if (test_element == EL_PENGUIN)
12872     {
12873       kill_x = test_x;
12874       kill_y = test_y;
12875     }
12876   }
12877
12878   if (kill_x != -1 || kill_y != -1)
12879   {
12880     if (IS_PLAYER(kill_x, kill_y))
12881     {
12882       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12883
12884       if (player->shield_deadly_time_left > 0 &&
12885           !IS_INDESTRUCTIBLE(bad_element))
12886         Bang(bad_x, bad_y);
12887       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12888         KillPlayer(player);
12889     }
12890     else
12891       Bang(kill_x, kill_y);
12892   }
12893 }
12894
12895 void TestIfPlayerTouchesBadThing(int x, int y)
12896 {
12897   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12898 }
12899
12900 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12901 {
12902   TestIfGoodThingHitsBadThing(x, y, move_dir);
12903 }
12904
12905 void TestIfBadThingTouchesPlayer(int x, int y)
12906 {
12907   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12908 }
12909
12910 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12911 {
12912   TestIfBadThingHitsGoodThing(x, y, move_dir);
12913 }
12914
12915 void TestIfFriendTouchesBadThing(int x, int y)
12916 {
12917   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12918 }
12919
12920 void TestIfBadThingTouchesFriend(int x, int y)
12921 {
12922   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12923 }
12924
12925 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12926 {
12927   int i, kill_x = bad_x, kill_y = bad_y;
12928   static int xy[4][2] =
12929   {
12930     { 0, -1 },
12931     { -1, 0 },
12932     { +1, 0 },
12933     { 0, +1 }
12934   };
12935
12936   for (i = 0; i < NUM_DIRECTIONS; i++)
12937   {
12938     int x, y, element;
12939
12940     x = bad_x + xy[i][0];
12941     y = bad_y + xy[i][1];
12942     if (!IN_LEV_FIELD(x, y))
12943       continue;
12944
12945     element = Feld[x][y];
12946     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12947         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12948     {
12949       kill_x = x;
12950       kill_y = y;
12951       break;
12952     }
12953   }
12954
12955   if (kill_x != bad_x || kill_y != bad_y)
12956     Bang(bad_x, bad_y);
12957 }
12958
12959 void KillPlayer(struct PlayerInfo *player)
12960 {
12961   int jx = player->jx, jy = player->jy;
12962
12963   if (!player->active)
12964     return;
12965
12966 #if 0
12967   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12968          player->killed, player->active, player->reanimated);
12969 #endif
12970
12971   /* the following code was introduced to prevent an infinite loop when calling
12972      -> Bang()
12973      -> CheckTriggeredElementChangeExt()
12974      -> ExecuteCustomElementAction()
12975      -> KillPlayer()
12976      -> (infinitely repeating the above sequence of function calls)
12977      which occurs when killing the player while having a CE with the setting
12978      "kill player X when explosion of <player X>"; the solution using a new
12979      field "player->killed" was chosen for backwards compatibility, although
12980      clever use of the fields "player->active" etc. would probably also work */
12981 #if 1
12982   if (player->killed)
12983     return;
12984 #endif
12985
12986   player->killed = TRUE;
12987
12988   /* remove accessible field at the player's position */
12989   Feld[jx][jy] = EL_EMPTY;
12990
12991   /* deactivate shield (else Bang()/Explode() would not work right) */
12992   player->shield_normal_time_left = 0;
12993   player->shield_deadly_time_left = 0;
12994
12995 #if 0
12996   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12997          player->killed, player->active, player->reanimated);
12998 #endif
12999
13000   Bang(jx, jy);
13001
13002 #if 0
13003   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13004          player->killed, player->active, player->reanimated);
13005 #endif
13006
13007   if (player->reanimated)       /* killed player may have been reanimated */
13008     player->killed = player->reanimated = FALSE;
13009   else
13010     BuryPlayer(player);
13011 }
13012
13013 static void KillPlayerUnlessEnemyProtected(int x, int y)
13014 {
13015   if (!PLAYER_ENEMY_PROTECTED(x, y))
13016     KillPlayer(PLAYERINFO(x, y));
13017 }
13018
13019 static void KillPlayerUnlessExplosionProtected(int x, int y)
13020 {
13021   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13022     KillPlayer(PLAYERINFO(x, y));
13023 }
13024
13025 void BuryPlayer(struct PlayerInfo *player)
13026 {
13027   int jx = player->jx, jy = player->jy;
13028
13029   if (!player->active)
13030     return;
13031
13032   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13033   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13034
13035   player->GameOver = TRUE;
13036   RemovePlayer(player);
13037 }
13038
13039 void RemovePlayer(struct PlayerInfo *player)
13040 {
13041   int jx = player->jx, jy = player->jy;
13042   int i, found = FALSE;
13043
13044   player->present = FALSE;
13045   player->active = FALSE;
13046
13047   if (!ExplodeField[jx][jy])
13048     StorePlayer[jx][jy] = 0;
13049
13050   if (player->is_moving)
13051     TEST_DrawLevelField(player->last_jx, player->last_jy);
13052
13053   for (i = 0; i < MAX_PLAYERS; i++)
13054     if (stored_player[i].active)
13055       found = TRUE;
13056
13057   if (!found)
13058     AllPlayersGone = TRUE;
13059
13060   ExitX = ZX = jx;
13061   ExitY = ZY = jy;
13062 }
13063
13064 static void setFieldForSnapping(int x, int y, int element, int direction)
13065 {
13066   struct ElementInfo *ei = &element_info[element];
13067   int direction_bit = MV_DIR_TO_BIT(direction);
13068   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13069   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13070                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13071
13072   Feld[x][y] = EL_ELEMENT_SNAPPING;
13073   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13074
13075   ResetGfxAnimation(x, y);
13076
13077   GfxElement[x][y] = element;
13078   GfxAction[x][y] = action;
13079   GfxDir[x][y] = direction;
13080   GfxFrame[x][y] = -1;
13081 }
13082
13083 /*
13084   =============================================================================
13085   checkDiagonalPushing()
13086   -----------------------------------------------------------------------------
13087   check if diagonal input device direction results in pushing of object
13088   (by checking if the alternative direction is walkable, diggable, ...)
13089   =============================================================================
13090 */
13091
13092 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13093                                     int x, int y, int real_dx, int real_dy)
13094 {
13095   int jx, jy, dx, dy, xx, yy;
13096
13097   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13098     return TRUE;
13099
13100   /* diagonal direction: check alternative direction */
13101   jx = player->jx;
13102   jy = player->jy;
13103   dx = x - jx;
13104   dy = y - jy;
13105   xx = jx + (dx == 0 ? real_dx : 0);
13106   yy = jy + (dy == 0 ? real_dy : 0);
13107
13108   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13109 }
13110
13111 /*
13112   =============================================================================
13113   DigField()
13114   -----------------------------------------------------------------------------
13115   x, y:                 field next to player (non-diagonal) to try to dig to
13116   real_dx, real_dy:     direction as read from input device (can be diagonal)
13117   =============================================================================
13118 */
13119
13120 static int DigField(struct PlayerInfo *player,
13121                     int oldx, int oldy, int x, int y,
13122                     int real_dx, int real_dy, int mode)
13123 {
13124   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13125   boolean player_was_pushing = player->is_pushing;
13126   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13127   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13128   int jx = oldx, jy = oldy;
13129   int dx = x - jx, dy = y - jy;
13130   int nextx = x + dx, nexty = y + dy;
13131   int move_direction = (dx == -1 ? MV_LEFT  :
13132                         dx == +1 ? MV_RIGHT :
13133                         dy == -1 ? MV_UP    :
13134                         dy == +1 ? MV_DOWN  : MV_NONE);
13135   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13136   int dig_side = MV_DIR_OPPOSITE(move_direction);
13137   int old_element = Feld[jx][jy];
13138   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13139   int collect_count;
13140
13141   if (is_player)                /* function can also be called by EL_PENGUIN */
13142   {
13143     if (player->MovPos == 0)
13144     {
13145       player->is_digging = FALSE;
13146       player->is_collecting = FALSE;
13147     }
13148
13149     if (player->MovPos == 0)    /* last pushing move finished */
13150       player->is_pushing = FALSE;
13151
13152     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13153     {
13154       player->is_switching = FALSE;
13155       player->push_delay = -1;
13156
13157       return MP_NO_ACTION;
13158     }
13159   }
13160
13161   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13162     old_element = Back[jx][jy];
13163
13164   /* in case of element dropped at player position, check background */
13165   else if (Back[jx][jy] != EL_EMPTY &&
13166            game.engine_version >= VERSION_IDENT(2,2,0,0))
13167     old_element = Back[jx][jy];
13168
13169   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13170     return MP_NO_ACTION;        /* field has no opening in this direction */
13171
13172   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13173     return MP_NO_ACTION;        /* field has no opening in this direction */
13174
13175   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13176   {
13177     SplashAcid(x, y);
13178
13179     Feld[jx][jy] = player->artwork_element;
13180     InitMovingField(jx, jy, MV_DOWN);
13181     Store[jx][jy] = EL_ACID;
13182     ContinueMoving(jx, jy);
13183     BuryPlayer(player);
13184
13185     return MP_DONT_RUN_INTO;
13186   }
13187
13188   if (player_can_move && DONT_RUN_INTO(element))
13189   {
13190     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13191
13192     return MP_DONT_RUN_INTO;
13193   }
13194
13195   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13196     return MP_NO_ACTION;
13197
13198   collect_count = element_info[element].collect_count_initial;
13199
13200   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13201     return MP_NO_ACTION;
13202
13203   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13204     player_can_move = player_can_move_or_snap;
13205
13206   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13207       game.engine_version >= VERSION_IDENT(2,2,0,0))
13208   {
13209     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13210                                player->index_bit, dig_side);
13211     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13212                                         player->index_bit, dig_side);
13213
13214     if (element == EL_DC_LANDMINE)
13215       Bang(x, y);
13216
13217     if (Feld[x][y] != element)          /* field changed by snapping */
13218       return MP_ACTION;
13219
13220     return MP_NO_ACTION;
13221   }
13222
13223   if (player->gravity && is_player && !player->is_auto_moving &&
13224       canFallDown(player) && move_direction != MV_DOWN &&
13225       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13226     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13227
13228   if (player_can_move &&
13229       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13230   {
13231     int sound_element = SND_ELEMENT(element);
13232     int sound_action = ACTION_WALKING;
13233
13234     if (IS_RND_GATE(element))
13235     {
13236       if (!player->key[RND_GATE_NR(element)])
13237         return MP_NO_ACTION;
13238     }
13239     else if (IS_RND_GATE_GRAY(element))
13240     {
13241       if (!player->key[RND_GATE_GRAY_NR(element)])
13242         return MP_NO_ACTION;
13243     }
13244     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13245     {
13246       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13247         return MP_NO_ACTION;
13248     }
13249     else if (element == EL_EXIT_OPEN ||
13250              element == EL_EM_EXIT_OPEN ||
13251              element == EL_EM_EXIT_OPENING ||
13252              element == EL_STEEL_EXIT_OPEN ||
13253              element == EL_EM_STEEL_EXIT_OPEN ||
13254              element == EL_EM_STEEL_EXIT_OPENING ||
13255              element == EL_SP_EXIT_OPEN ||
13256              element == EL_SP_EXIT_OPENING)
13257     {
13258       sound_action = ACTION_PASSING;    /* player is passing exit */
13259     }
13260     else if (element == EL_EMPTY)
13261     {
13262       sound_action = ACTION_MOVING;             /* nothing to walk on */
13263     }
13264
13265     /* play sound from background or player, whatever is available */
13266     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13267       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13268     else
13269       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13270   }
13271   else if (player_can_move &&
13272            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13273   {
13274     if (!ACCESS_FROM(element, opposite_direction))
13275       return MP_NO_ACTION;      /* field not accessible from this direction */
13276
13277     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13278       return MP_NO_ACTION;
13279
13280     if (IS_EM_GATE(element))
13281     {
13282       if (!player->key[EM_GATE_NR(element)])
13283         return MP_NO_ACTION;
13284     }
13285     else if (IS_EM_GATE_GRAY(element))
13286     {
13287       if (!player->key[EM_GATE_GRAY_NR(element)])
13288         return MP_NO_ACTION;
13289     }
13290     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13291     {
13292       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13293         return MP_NO_ACTION;
13294     }
13295     else if (IS_EMC_GATE(element))
13296     {
13297       if (!player->key[EMC_GATE_NR(element)])
13298         return MP_NO_ACTION;
13299     }
13300     else if (IS_EMC_GATE_GRAY(element))
13301     {
13302       if (!player->key[EMC_GATE_GRAY_NR(element)])
13303         return MP_NO_ACTION;
13304     }
13305     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13306     {
13307       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13308         return MP_NO_ACTION;
13309     }
13310     else if (element == EL_DC_GATE_WHITE ||
13311              element == EL_DC_GATE_WHITE_GRAY ||
13312              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13313     {
13314       if (player->num_white_keys == 0)
13315         return MP_NO_ACTION;
13316
13317       player->num_white_keys--;
13318     }
13319     else if (IS_SP_PORT(element))
13320     {
13321       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13322           element == EL_SP_GRAVITY_PORT_RIGHT ||
13323           element == EL_SP_GRAVITY_PORT_UP ||
13324           element == EL_SP_GRAVITY_PORT_DOWN)
13325         player->gravity = !player->gravity;
13326       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13327                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13328                element == EL_SP_GRAVITY_ON_PORT_UP ||
13329                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13330         player->gravity = TRUE;
13331       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13332                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13333                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13334                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13335         player->gravity = FALSE;
13336     }
13337
13338     /* automatically move to the next field with double speed */
13339     player->programmed_action = move_direction;
13340
13341     if (player->move_delay_reset_counter == 0)
13342     {
13343       player->move_delay_reset_counter = 2;     /* two double speed steps */
13344
13345       DOUBLE_PLAYER_SPEED(player);
13346     }
13347
13348     PlayLevelSoundAction(x, y, ACTION_PASSING);
13349   }
13350   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13351   {
13352     RemoveField(x, y);
13353
13354     if (mode != DF_SNAP)
13355     {
13356       GfxElement[x][y] = GFX_ELEMENT(element);
13357       player->is_digging = TRUE;
13358     }
13359
13360     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13361
13362     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13363                                         player->index_bit, dig_side);
13364
13365     if (mode == DF_SNAP)
13366     {
13367       if (level.block_snap_field)
13368         setFieldForSnapping(x, y, element, move_direction);
13369       else
13370         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13371
13372       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13373                                           player->index_bit, dig_side);
13374     }
13375   }
13376   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13377   {
13378     RemoveField(x, y);
13379
13380     if (is_player && mode != DF_SNAP)
13381     {
13382       GfxElement[x][y] = element;
13383       player->is_collecting = TRUE;
13384     }
13385
13386     if (element == EL_SPEED_PILL)
13387     {
13388       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13389     }
13390     else if (element == EL_EXTRA_TIME && level.time > 0)
13391     {
13392       TimeLeft += level.extra_time;
13393
13394       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13395
13396       DisplayGameControlValues();
13397     }
13398     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13399     {
13400       player->shield_normal_time_left += level.shield_normal_time;
13401       if (element == EL_SHIELD_DEADLY)
13402         player->shield_deadly_time_left += level.shield_deadly_time;
13403     }
13404     else if (element == EL_DYNAMITE ||
13405              element == EL_EM_DYNAMITE ||
13406              element == EL_SP_DISK_RED)
13407     {
13408       if (player->inventory_size < MAX_INVENTORY_SIZE)
13409         player->inventory_element[player->inventory_size++] = element;
13410
13411       DrawGameDoorValues();
13412     }
13413     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13414     {
13415       player->dynabomb_count++;
13416       player->dynabombs_left++;
13417     }
13418     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13419     {
13420       player->dynabomb_size++;
13421     }
13422     else if (element == EL_DYNABOMB_INCREASE_POWER)
13423     {
13424       player->dynabomb_xl = TRUE;
13425     }
13426     else if (IS_KEY(element))
13427     {
13428       player->key[KEY_NR(element)] = TRUE;
13429
13430       DrawGameDoorValues();
13431     }
13432     else if (element == EL_DC_KEY_WHITE)
13433     {
13434       player->num_white_keys++;
13435
13436       /* display white keys? */
13437       /* DrawGameDoorValues(); */
13438     }
13439     else if (IS_ENVELOPE(element))
13440     {
13441       player->show_envelope = element;
13442     }
13443     else if (element == EL_EMC_LENSES)
13444     {
13445       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13446
13447       RedrawAllInvisibleElementsForLenses();
13448     }
13449     else if (element == EL_EMC_MAGNIFIER)
13450     {
13451       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13452
13453       RedrawAllInvisibleElementsForMagnifier();
13454     }
13455     else if (IS_DROPPABLE(element) ||
13456              IS_THROWABLE(element))     /* can be collected and dropped */
13457     {
13458       int i;
13459
13460       if (collect_count == 0)
13461         player->inventory_infinite_element = element;
13462       else
13463         for (i = 0; i < collect_count; i++)
13464           if (player->inventory_size < MAX_INVENTORY_SIZE)
13465             player->inventory_element[player->inventory_size++] = element;
13466
13467       DrawGameDoorValues();
13468     }
13469     else if (collect_count > 0)
13470     {
13471       local_player->gems_still_needed -= collect_count;
13472       if (local_player->gems_still_needed < 0)
13473         local_player->gems_still_needed = 0;
13474
13475       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13476
13477       DisplayGameControlValues();
13478     }
13479
13480     RaiseScoreElement(element);
13481     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13482
13483     if (is_player)
13484       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13485                                           player->index_bit, dig_side);
13486
13487     if (mode == DF_SNAP)
13488     {
13489       if (level.block_snap_field)
13490         setFieldForSnapping(x, y, element, move_direction);
13491       else
13492         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13493
13494       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13495                                           player->index_bit, dig_side);
13496     }
13497   }
13498   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13499   {
13500     if (mode == DF_SNAP && element != EL_BD_ROCK)
13501       return MP_NO_ACTION;
13502
13503     if (CAN_FALL(element) && dy)
13504       return MP_NO_ACTION;
13505
13506     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13507         !(element == EL_SPRING && level.use_spring_bug))
13508       return MP_NO_ACTION;
13509
13510     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13511         ((move_direction & MV_VERTICAL &&
13512           ((element_info[element].move_pattern & MV_LEFT &&
13513             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13514            (element_info[element].move_pattern & MV_RIGHT &&
13515             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13516          (move_direction & MV_HORIZONTAL &&
13517           ((element_info[element].move_pattern & MV_UP &&
13518             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13519            (element_info[element].move_pattern & MV_DOWN &&
13520             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13521       return MP_NO_ACTION;
13522
13523     /* do not push elements already moving away faster than player */
13524     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13525         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13526       return MP_NO_ACTION;
13527
13528     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13529     {
13530       if (player->push_delay_value == -1 || !player_was_pushing)
13531         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13532     }
13533     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13534     {
13535       if (player->push_delay_value == -1)
13536         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13537     }
13538     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13539     {
13540       if (!player->is_pushing)
13541         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13542     }
13543
13544     player->is_pushing = TRUE;
13545     player->is_active = TRUE;
13546
13547     if (!(IN_LEV_FIELD(nextx, nexty) &&
13548           (IS_FREE(nextx, nexty) ||
13549            (IS_SB_ELEMENT(element) &&
13550             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13551            (IS_CUSTOM_ELEMENT(element) &&
13552             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13553       return MP_NO_ACTION;
13554
13555     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13556       return MP_NO_ACTION;
13557
13558     if (player->push_delay == -1)       /* new pushing; restart delay */
13559       player->push_delay = 0;
13560
13561     if (player->push_delay < player->push_delay_value &&
13562         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13563         element != EL_SPRING && element != EL_BALLOON)
13564     {
13565       /* make sure that there is no move delay before next try to push */
13566       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13567         player->move_delay = 0;
13568
13569       return MP_NO_ACTION;
13570     }
13571
13572     if (IS_CUSTOM_ELEMENT(element) &&
13573         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13574     {
13575       if (!DigFieldByCE(nextx, nexty, element))
13576         return MP_NO_ACTION;
13577     }
13578
13579     if (IS_SB_ELEMENT(element))
13580     {
13581       if (element == EL_SOKOBAN_FIELD_FULL)
13582       {
13583         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13584         local_player->sokobanfields_still_needed++;
13585       }
13586
13587       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13588       {
13589         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13590         local_player->sokobanfields_still_needed--;
13591       }
13592
13593       Feld[x][y] = EL_SOKOBAN_OBJECT;
13594
13595       if (Back[x][y] == Back[nextx][nexty])
13596         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13597       else if (Back[x][y] != 0)
13598         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13599                                     ACTION_EMPTYING);
13600       else
13601         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13602                                     ACTION_FILLING);
13603
13604       if (local_player->sokobanfields_still_needed == 0 &&
13605           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13606       {
13607         PlayerWins(player);
13608
13609         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13610       }
13611     }
13612     else
13613       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13614
13615     InitMovingField(x, y, move_direction);
13616     GfxAction[x][y] = ACTION_PUSHING;
13617
13618     if (mode == DF_SNAP)
13619       ContinueMoving(x, y);
13620     else
13621       MovPos[x][y] = (dx != 0 ? dx : dy);
13622
13623     Pushed[x][y] = TRUE;
13624     Pushed[nextx][nexty] = TRUE;
13625
13626     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13627       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13628     else
13629       player->push_delay_value = -1;    /* get new value later */
13630
13631     /* check for element change _after_ element has been pushed */
13632     if (game.use_change_when_pushing_bug)
13633     {
13634       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13635                                  player->index_bit, dig_side);
13636       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13637                                           player->index_bit, dig_side);
13638     }
13639   }
13640   else if (IS_SWITCHABLE(element))
13641   {
13642     if (PLAYER_SWITCHING(player, x, y))
13643     {
13644       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13645                                           player->index_bit, dig_side);
13646
13647       return MP_ACTION;
13648     }
13649
13650     player->is_switching = TRUE;
13651     player->switch_x = x;
13652     player->switch_y = y;
13653
13654     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13655
13656     if (element == EL_ROBOT_WHEEL)
13657     {
13658       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13659       ZX = x;
13660       ZY = y;
13661
13662       game.robot_wheel_active = TRUE;
13663
13664       TEST_DrawLevelField(x, y);
13665     }
13666     else if (element == EL_SP_TERMINAL)
13667     {
13668       int xx, yy;
13669
13670       SCAN_PLAYFIELD(xx, yy)
13671       {
13672         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13673           Bang(xx, yy);
13674         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13675           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13676       }
13677     }
13678     else if (IS_BELT_SWITCH(element))
13679     {
13680       ToggleBeltSwitch(x, y);
13681     }
13682     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13683              element == EL_SWITCHGATE_SWITCH_DOWN ||
13684              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13685              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13686     {
13687       ToggleSwitchgateSwitch(x, y);
13688     }
13689     else if (element == EL_LIGHT_SWITCH ||
13690              element == EL_LIGHT_SWITCH_ACTIVE)
13691     {
13692       ToggleLightSwitch(x, y);
13693     }
13694     else if (element == EL_TIMEGATE_SWITCH ||
13695              element == EL_DC_TIMEGATE_SWITCH)
13696     {
13697       ActivateTimegateSwitch(x, y);
13698     }
13699     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13700              element == EL_BALLOON_SWITCH_RIGHT ||
13701              element == EL_BALLOON_SWITCH_UP    ||
13702              element == EL_BALLOON_SWITCH_DOWN  ||
13703              element == EL_BALLOON_SWITCH_NONE  ||
13704              element == EL_BALLOON_SWITCH_ANY)
13705     {
13706       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13707                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13708                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13709                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13710                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13711                              move_direction);
13712     }
13713     else if (element == EL_LAMP)
13714     {
13715       Feld[x][y] = EL_LAMP_ACTIVE;
13716       local_player->lights_still_needed--;
13717
13718       ResetGfxAnimation(x, y);
13719       TEST_DrawLevelField(x, y);
13720     }
13721     else if (element == EL_TIME_ORB_FULL)
13722     {
13723       Feld[x][y] = EL_TIME_ORB_EMPTY;
13724
13725       if (level.time > 0 || level.use_time_orb_bug)
13726       {
13727         TimeLeft += level.time_orb_time;
13728         game.no_time_limit = FALSE;
13729
13730         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13731
13732         DisplayGameControlValues();
13733       }
13734
13735       ResetGfxAnimation(x, y);
13736       TEST_DrawLevelField(x, y);
13737     }
13738     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13739              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13740     {
13741       int xx, yy;
13742
13743       game.ball_state = !game.ball_state;
13744
13745       SCAN_PLAYFIELD(xx, yy)
13746       {
13747         int e = Feld[xx][yy];
13748
13749         if (game.ball_state)
13750         {
13751           if (e == EL_EMC_MAGIC_BALL)
13752             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13753           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13754             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13755         }
13756         else
13757         {
13758           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13759             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13760           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13761             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13762         }
13763       }
13764     }
13765
13766     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13767                                         player->index_bit, dig_side);
13768
13769     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13770                                         player->index_bit, dig_side);
13771
13772     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13773                                         player->index_bit, dig_side);
13774
13775     return MP_ACTION;
13776   }
13777   else
13778   {
13779     if (!PLAYER_SWITCHING(player, x, y))
13780     {
13781       player->is_switching = TRUE;
13782       player->switch_x = x;
13783       player->switch_y = y;
13784
13785       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13786                                  player->index_bit, dig_side);
13787       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13788                                           player->index_bit, dig_side);
13789
13790       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13791                                  player->index_bit, dig_side);
13792       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13793                                           player->index_bit, dig_side);
13794     }
13795
13796     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13797                                player->index_bit, dig_side);
13798     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13799                                         player->index_bit, dig_side);
13800
13801     return MP_NO_ACTION;
13802   }
13803
13804   player->push_delay = -1;
13805
13806   if (is_player)                /* function can also be called by EL_PENGUIN */
13807   {
13808     if (Feld[x][y] != element)          /* really digged/collected something */
13809     {
13810       player->is_collecting = !player->is_digging;
13811       player->is_active = TRUE;
13812     }
13813   }
13814
13815   return MP_MOVING;
13816 }
13817
13818 static boolean DigFieldByCE(int x, int y, int digging_element)
13819 {
13820   int element = Feld[x][y];
13821
13822   if (!IS_FREE(x, y))
13823   {
13824     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13825                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13826                   ACTION_BREAKING);
13827
13828     /* no element can dig solid indestructible elements */
13829     if (IS_INDESTRUCTIBLE(element) &&
13830         !IS_DIGGABLE(element) &&
13831         !IS_COLLECTIBLE(element))
13832       return FALSE;
13833
13834     if (AmoebaNr[x][y] &&
13835         (element == EL_AMOEBA_FULL ||
13836          element == EL_BD_AMOEBA ||
13837          element == EL_AMOEBA_GROWING))
13838     {
13839       AmoebaCnt[AmoebaNr[x][y]]--;
13840       AmoebaCnt2[AmoebaNr[x][y]]--;
13841     }
13842
13843     if (IS_MOVING(x, y))
13844       RemoveMovingField(x, y);
13845     else
13846     {
13847       RemoveField(x, y);
13848       TEST_DrawLevelField(x, y);
13849     }
13850
13851     /* if digged element was about to explode, prevent the explosion */
13852     ExplodeField[x][y] = EX_TYPE_NONE;
13853
13854     PlayLevelSoundAction(x, y, action);
13855   }
13856
13857   Store[x][y] = EL_EMPTY;
13858
13859   /* this makes it possible to leave the removed element again */
13860   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13861     Store[x][y] = element;
13862
13863   return TRUE;
13864 }
13865
13866 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13867 {
13868   int jx = player->jx, jy = player->jy;
13869   int x = jx + dx, y = jy + dy;
13870   int snap_direction = (dx == -1 ? MV_LEFT  :
13871                         dx == +1 ? MV_RIGHT :
13872                         dy == -1 ? MV_UP    :
13873                         dy == +1 ? MV_DOWN  : MV_NONE);
13874   boolean can_continue_snapping = (level.continuous_snapping &&
13875                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13876
13877   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13878     return FALSE;
13879
13880   if (!player->active || !IN_LEV_FIELD(x, y))
13881     return FALSE;
13882
13883   if (dx && dy)
13884     return FALSE;
13885
13886   if (!dx && !dy)
13887   {
13888     if (player->MovPos == 0)
13889       player->is_pushing = FALSE;
13890
13891     player->is_snapping = FALSE;
13892
13893     if (player->MovPos == 0)
13894     {
13895       player->is_moving = FALSE;
13896       player->is_digging = FALSE;
13897       player->is_collecting = FALSE;
13898     }
13899
13900     return FALSE;
13901   }
13902
13903   /* prevent snapping with already pressed snap key when not allowed */
13904   if (player->is_snapping && !can_continue_snapping)
13905     return FALSE;
13906
13907   player->MovDir = snap_direction;
13908
13909   if (player->MovPos == 0)
13910   {
13911     player->is_moving = FALSE;
13912     player->is_digging = FALSE;
13913     player->is_collecting = FALSE;
13914   }
13915
13916   player->is_dropping = FALSE;
13917   player->is_dropping_pressed = FALSE;
13918   player->drop_pressed_delay = 0;
13919
13920   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13921     return FALSE;
13922
13923   player->is_snapping = TRUE;
13924   player->is_active = TRUE;
13925
13926   if (player->MovPos == 0)
13927   {
13928     player->is_moving = FALSE;
13929     player->is_digging = FALSE;
13930     player->is_collecting = FALSE;
13931   }
13932
13933   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13934     TEST_DrawLevelField(player->last_jx, player->last_jy);
13935
13936   TEST_DrawLevelField(x, y);
13937
13938   return TRUE;
13939 }
13940
13941 static boolean DropElement(struct PlayerInfo *player)
13942 {
13943   int old_element, new_element;
13944   int dropx = player->jx, dropy = player->jy;
13945   int drop_direction = player->MovDir;
13946   int drop_side = drop_direction;
13947   int drop_element = get_next_dropped_element(player);
13948
13949   player->is_dropping_pressed = TRUE;
13950
13951   /* do not drop an element on top of another element; when holding drop key
13952      pressed without moving, dropped element must move away before the next
13953      element can be dropped (this is especially important if the next element
13954      is dynamite, which can be placed on background for historical reasons) */
13955   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13956     return MP_ACTION;
13957
13958   if (IS_THROWABLE(drop_element))
13959   {
13960     dropx += GET_DX_FROM_DIR(drop_direction);
13961     dropy += GET_DY_FROM_DIR(drop_direction);
13962
13963     if (!IN_LEV_FIELD(dropx, dropy))
13964       return FALSE;
13965   }
13966
13967   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13968   new_element = drop_element;           /* default: no change when dropping */
13969
13970   /* check if player is active, not moving and ready to drop */
13971   if (!player->active || player->MovPos || player->drop_delay > 0)
13972     return FALSE;
13973
13974   /* check if player has anything that can be dropped */
13975   if (new_element == EL_UNDEFINED)
13976     return FALSE;
13977
13978   /* check if drop key was pressed long enough for EM style dynamite */
13979   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13980     return FALSE;
13981
13982   /* check if anything can be dropped at the current position */
13983   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13984     return FALSE;
13985
13986   /* collected custom elements can only be dropped on empty fields */
13987   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13988     return FALSE;
13989
13990   if (old_element != EL_EMPTY)
13991     Back[dropx][dropy] = old_element;   /* store old element on this field */
13992
13993   ResetGfxAnimation(dropx, dropy);
13994   ResetRandomAnimationValue(dropx, dropy);
13995
13996   if (player->inventory_size > 0 ||
13997       player->inventory_infinite_element != EL_UNDEFINED)
13998   {
13999     if (player->inventory_size > 0)
14000     {
14001       player->inventory_size--;
14002
14003       DrawGameDoorValues();
14004
14005       if (new_element == EL_DYNAMITE)
14006         new_element = EL_DYNAMITE_ACTIVE;
14007       else if (new_element == EL_EM_DYNAMITE)
14008         new_element = EL_EM_DYNAMITE_ACTIVE;
14009       else if (new_element == EL_SP_DISK_RED)
14010         new_element = EL_SP_DISK_RED_ACTIVE;
14011     }
14012
14013     Feld[dropx][dropy] = new_element;
14014
14015     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14016       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14017                           el2img(Feld[dropx][dropy]), 0);
14018
14019     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14020
14021     /* needed if previous element just changed to "empty" in the last frame */
14022     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14023
14024     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14025                                player->index_bit, drop_side);
14026     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14027                                         CE_PLAYER_DROPS_X,
14028                                         player->index_bit, drop_side);
14029
14030     TestIfElementTouchesCustomElement(dropx, dropy);
14031   }
14032   else          /* player is dropping a dyna bomb */
14033   {
14034     player->dynabombs_left--;
14035
14036     Feld[dropx][dropy] = new_element;
14037
14038     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14039       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14040                           el2img(Feld[dropx][dropy]), 0);
14041
14042     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14043   }
14044
14045   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14046     InitField_WithBug1(dropx, dropy, FALSE);
14047
14048   new_element = Feld[dropx][dropy];     /* element might have changed */
14049
14050   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14051       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14052   {
14053     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14054       MovDir[dropx][dropy] = drop_direction;
14055
14056     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14057
14058     /* do not cause impact style collision by dropping elements that can fall */
14059     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14060   }
14061
14062   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14063   player->is_dropping = TRUE;
14064
14065   player->drop_pressed_delay = 0;
14066   player->is_dropping_pressed = FALSE;
14067
14068   player->drop_x = dropx;
14069   player->drop_y = dropy;
14070
14071   return TRUE;
14072 }
14073
14074 /* ------------------------------------------------------------------------- */
14075 /* game sound playing functions                                              */
14076 /* ------------------------------------------------------------------------- */
14077
14078 static int *loop_sound_frame = NULL;
14079 static int *loop_sound_volume = NULL;
14080
14081 void InitPlayLevelSound()
14082 {
14083   int num_sounds = getSoundListSize();
14084
14085   checked_free(loop_sound_frame);
14086   checked_free(loop_sound_volume);
14087
14088   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14089   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14090 }
14091
14092 static void PlayLevelSound(int x, int y, int nr)
14093 {
14094   int sx = SCREENX(x), sy = SCREENY(y);
14095   int volume, stereo_position;
14096   int max_distance = 8;
14097   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14098
14099   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14100       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14101     return;
14102
14103   if (!IN_LEV_FIELD(x, y) ||
14104       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14105       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14106     return;
14107
14108   volume = SOUND_MAX_VOLUME;
14109
14110   if (!IN_SCR_FIELD(sx, sy))
14111   {
14112     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14113     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14114
14115     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14116   }
14117
14118   stereo_position = (SOUND_MAX_LEFT +
14119                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14120                      (SCR_FIELDX + 2 * max_distance));
14121
14122   if (IS_LOOP_SOUND(nr))
14123   {
14124     /* This assures that quieter loop sounds do not overwrite louder ones,
14125        while restarting sound volume comparison with each new game frame. */
14126
14127     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14128       return;
14129
14130     loop_sound_volume[nr] = volume;
14131     loop_sound_frame[nr] = FrameCounter;
14132   }
14133
14134   PlaySoundExt(nr, volume, stereo_position, type);
14135 }
14136
14137 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14138 {
14139   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14140                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14141                  y < LEVELY(BY1) ? LEVELY(BY1) :
14142                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14143                  sound_action);
14144 }
14145
14146 static void PlayLevelSoundAction(int x, int y, int action)
14147 {
14148   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14149 }
14150
14151 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14152 {
14153   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14154
14155   if (sound_effect != SND_UNDEFINED)
14156     PlayLevelSound(x, y, sound_effect);
14157 }
14158
14159 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14160                                               int action)
14161 {
14162   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14163
14164   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14165     PlayLevelSound(x, y, sound_effect);
14166 }
14167
14168 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14169 {
14170   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].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 StopLevelSoundActionIfLoop(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     StopSound(sound_effect);
14182 }
14183
14184 static void PlayLevelMusic()
14185 {
14186   if (levelset.music[level_nr] != MUS_UNDEFINED)
14187     PlayMusic(levelset.music[level_nr]);        /* from config file */
14188   else
14189     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14190 }
14191
14192 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14193 {
14194   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14195   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14196   int x = xx - 1 - offset;
14197   int y = yy - 1 - offset;
14198
14199   switch (sample)
14200   {
14201     case SAMPLE_blank:
14202       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14203       break;
14204
14205     case SAMPLE_roll:
14206       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14207       break;
14208
14209     case SAMPLE_stone:
14210       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14211       break;
14212
14213     case SAMPLE_nut:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14215       break;
14216
14217     case SAMPLE_crack:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14219       break;
14220
14221     case SAMPLE_bug:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14223       break;
14224
14225     case SAMPLE_tank:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14227       break;
14228
14229     case SAMPLE_android_clone:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14231       break;
14232
14233     case SAMPLE_android_move:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14235       break;
14236
14237     case SAMPLE_spring:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14239       break;
14240
14241     case SAMPLE_slurp:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14243       break;
14244
14245     case SAMPLE_eater:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14247       break;
14248
14249     case SAMPLE_eater_eat:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14251       break;
14252
14253     case SAMPLE_alien:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14255       break;
14256
14257     case SAMPLE_collect:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14259       break;
14260
14261     case SAMPLE_diamond:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14263       break;
14264
14265     case SAMPLE_squash:
14266       /* !!! CHECK THIS !!! */
14267 #if 1
14268       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14269 #else
14270       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14271 #endif
14272       break;
14273
14274     case SAMPLE_wonderfall:
14275       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14276       break;
14277
14278     case SAMPLE_drip:
14279       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14280       break;
14281
14282     case SAMPLE_push:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14284       break;
14285
14286     case SAMPLE_dirt:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14288       break;
14289
14290     case SAMPLE_acid:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14292       break;
14293
14294     case SAMPLE_ball:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14296       break;
14297
14298     case SAMPLE_grow:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14300       break;
14301
14302     case SAMPLE_wonder:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14304       break;
14305
14306     case SAMPLE_door:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14308       break;
14309
14310     case SAMPLE_exit_open:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14312       break;
14313
14314     case SAMPLE_exit_leave:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14316       break;
14317
14318     case SAMPLE_dynamite:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14320       break;
14321
14322     case SAMPLE_tick:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14324       break;
14325
14326     case SAMPLE_press:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14328       break;
14329
14330     case SAMPLE_wheel:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14332       break;
14333
14334     case SAMPLE_boom:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14336       break;
14337
14338     case SAMPLE_die:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14340       break;
14341
14342     case SAMPLE_time:
14343       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14344       break;
14345
14346     default:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14348       break;
14349   }
14350 }
14351
14352 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14353 {
14354   int element = map_element_SP_to_RND(element_sp);
14355   int action = map_action_SP_to_RND(action_sp);
14356   int offset = (setup.sp_show_border_elements ? 0 : 1);
14357   int x = xx - offset;
14358   int y = yy - offset;
14359
14360   PlayLevelSoundElementAction(x, y, element, action);
14361 }
14362
14363 void RaiseScore(int value)
14364 {
14365   local_player->score += value;
14366
14367   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14368
14369   DisplayGameControlValues();
14370 }
14371
14372 void RaiseScoreElement(int element)
14373 {
14374   switch (element)
14375   {
14376     case EL_EMERALD:
14377     case EL_BD_DIAMOND:
14378     case EL_EMERALD_YELLOW:
14379     case EL_EMERALD_RED:
14380     case EL_EMERALD_PURPLE:
14381     case EL_SP_INFOTRON:
14382       RaiseScore(level.score[SC_EMERALD]);
14383       break;
14384     case EL_DIAMOND:
14385       RaiseScore(level.score[SC_DIAMOND]);
14386       break;
14387     case EL_CRYSTAL:
14388       RaiseScore(level.score[SC_CRYSTAL]);
14389       break;
14390     case EL_PEARL:
14391       RaiseScore(level.score[SC_PEARL]);
14392       break;
14393     case EL_BUG:
14394     case EL_BD_BUTTERFLY:
14395     case EL_SP_ELECTRON:
14396       RaiseScore(level.score[SC_BUG]);
14397       break;
14398     case EL_SPACESHIP:
14399     case EL_BD_FIREFLY:
14400     case EL_SP_SNIKSNAK:
14401       RaiseScore(level.score[SC_SPACESHIP]);
14402       break;
14403     case EL_YAMYAM:
14404     case EL_DARK_YAMYAM:
14405       RaiseScore(level.score[SC_YAMYAM]);
14406       break;
14407     case EL_ROBOT:
14408       RaiseScore(level.score[SC_ROBOT]);
14409       break;
14410     case EL_PACMAN:
14411       RaiseScore(level.score[SC_PACMAN]);
14412       break;
14413     case EL_NUT:
14414       RaiseScore(level.score[SC_NUT]);
14415       break;
14416     case EL_DYNAMITE:
14417     case EL_EM_DYNAMITE:
14418     case EL_SP_DISK_RED:
14419     case EL_DYNABOMB_INCREASE_NUMBER:
14420     case EL_DYNABOMB_INCREASE_SIZE:
14421     case EL_DYNABOMB_INCREASE_POWER:
14422       RaiseScore(level.score[SC_DYNAMITE]);
14423       break;
14424     case EL_SHIELD_NORMAL:
14425     case EL_SHIELD_DEADLY:
14426       RaiseScore(level.score[SC_SHIELD]);
14427       break;
14428     case EL_EXTRA_TIME:
14429       RaiseScore(level.extra_time_score);
14430       break;
14431     case EL_KEY_1:
14432     case EL_KEY_2:
14433     case EL_KEY_3:
14434     case EL_KEY_4:
14435     case EL_EM_KEY_1:
14436     case EL_EM_KEY_2:
14437     case EL_EM_KEY_3:
14438     case EL_EM_KEY_4:
14439     case EL_EMC_KEY_5:
14440     case EL_EMC_KEY_6:
14441     case EL_EMC_KEY_7:
14442     case EL_EMC_KEY_8:
14443     case EL_DC_KEY_WHITE:
14444       RaiseScore(level.score[SC_KEY]);
14445       break;
14446     default:
14447       RaiseScore(element_info[element].collect_score);
14448       break;
14449   }
14450 }
14451
14452 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14453 {
14454   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14455   {
14456     /* closing door required in case of envelope style request dialogs */
14457     if (!skip_request)
14458       CloseDoor(DOOR_CLOSE_1);
14459
14460 #if defined(NETWORK_AVALIABLE)
14461     if (options.network)
14462       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14463     else
14464 #endif
14465     {
14466       if (quick_quit)
14467       {
14468         FadeSkipNextFadeIn();
14469
14470         game_status = GAME_MODE_MAIN;
14471
14472         DrawAndFadeInMainMenu(REDRAW_FIELD);
14473       }
14474       else
14475       {
14476         game_status = GAME_MODE_MAIN;
14477
14478         DrawAndFadeInMainMenu(REDRAW_FIELD);
14479       }
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
14772   game.snapshot.changed_action = FALSE;
14773
14774   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14775       tape.quick_resume ||
14776       !save_snapshot)
14777     return FALSE;
14778
14779   ListNode *buffers = SaveEngineSnapshotBuffers();
14780
14781   /* finally save all snapshot buffers to snapshot list */
14782   SaveSnapshotToList(buffers);
14783
14784   return TRUE;
14785 }
14786
14787 boolean SaveEngineSnapshotToList()
14788 {
14789   return SaveEngineSnapshotToListExt(FALSE);
14790 }
14791
14792 void SaveEngineSnapshotToListInitial()
14793 {
14794   FreeEngineSnapshotList();
14795
14796   SaveEngineSnapshotToListExt(TRUE);
14797 }
14798
14799 void LoadEngineSnapshotValues()
14800 {
14801   /* restore special values from snapshot structure */
14802
14803   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14804     LoadEngineSnapshotValues_RND();
14805   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14806     LoadEngineSnapshotValues_EM();
14807   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14808     LoadEngineSnapshotValues_SP();
14809 }
14810
14811 void LoadEngineSnapshotSingle()
14812 {
14813   LoadSnapshotSingle();
14814
14815   LoadEngineSnapshotValues();
14816 }
14817
14818 void LoadEngineSnapshot_Undo(int steps)
14819 {
14820   LoadSnapshotFromList_Older(steps);
14821
14822   LoadEngineSnapshotValues();
14823 }
14824
14825 void LoadEngineSnapshot_Redo(int steps)
14826 {
14827   LoadSnapshotFromList_Newer(steps);
14828
14829   LoadEngineSnapshotValues();
14830 }
14831
14832 boolean CheckEngineSnapshotSingle()
14833 {
14834   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14835           snapshot_level_nr == level_nr);
14836 }
14837
14838 boolean CheckEngineSnapshotList()
14839 {
14840   return CheckSnapshotList();
14841 }
14842
14843
14844 /* ---------- new game button stuff ---------------------------------------- */
14845
14846 static struct
14847 {
14848   int graphic;
14849   struct XY *pos;
14850   int gadget_id;
14851   char *infotext;
14852 } gamebutton_info[NUM_GAME_BUTTONS] =
14853 {
14854   {
14855     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14856     GAME_CTRL_ID_STOP,                  "stop game"
14857   },
14858   {
14859     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14860     GAME_CTRL_ID_PAUSE,                 "pause game"
14861   },
14862   {
14863     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14864     GAME_CTRL_ID_PLAY,                  "play game"
14865   },
14866   {
14867     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14868     GAME_CTRL_ID_UNDO,                  "undo step"
14869   },
14870   {
14871     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14872     GAME_CTRL_ID_REDO,                  "redo step"
14873   },
14874   {
14875     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14876     GAME_CTRL_ID_SAVE,                  "save game"
14877   },
14878   {
14879     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14880     GAME_CTRL_ID_LOAD,                  "load game"
14881   },
14882   {
14883     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14884     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14885   },
14886   {
14887     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14888     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14889   },
14890   {
14891     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14892     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14893   }
14894 };
14895
14896 void CreateGameButtons()
14897 {
14898   int i;
14899
14900   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14901   {
14902     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14903     struct XY *pos = gamebutton_info[i].pos;
14904     struct GadgetInfo *gi;
14905     int button_type;
14906     boolean checked;
14907     unsigned int event_mask;
14908     int base_x = (tape.show_game_buttons ? VX : DX);
14909     int base_y = (tape.show_game_buttons ? VY : DY);
14910     int gd_x   = gfx->src_x;
14911     int gd_y   = gfx->src_y;
14912     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14913     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14914     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14915     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14916     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14917     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14918     int id = i;
14919
14920     if (gfx->bitmap == NULL)
14921     {
14922       game_gadget[id] = NULL;
14923
14924       continue;
14925     }
14926
14927     if (id == GAME_CTRL_ID_STOP ||
14928         id == GAME_CTRL_ID_PAUSE ||
14929         id == GAME_CTRL_ID_PLAY ||
14930         id == GAME_CTRL_ID_SAVE ||
14931         id == GAME_CTRL_ID_LOAD)
14932     {
14933       button_type = GD_TYPE_NORMAL_BUTTON;
14934       checked = FALSE;
14935       event_mask = GD_EVENT_RELEASED;
14936     }
14937     else if (id == GAME_CTRL_ID_UNDO ||
14938              id == GAME_CTRL_ID_REDO)
14939     {
14940       button_type = GD_TYPE_NORMAL_BUTTON;
14941       checked = FALSE;
14942       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14943     }
14944     else
14945     {
14946       button_type = GD_TYPE_CHECK_BUTTON;
14947       checked =
14948         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14949          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14950          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14951       event_mask = GD_EVENT_PRESSED;
14952     }
14953
14954     gi = CreateGadget(GDI_CUSTOM_ID, id,
14955                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14956                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14957                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14958                       GDI_WIDTH, gfx->width,
14959                       GDI_HEIGHT, gfx->height,
14960                       GDI_TYPE, button_type,
14961                       GDI_STATE, GD_BUTTON_UNPRESSED,
14962                       GDI_CHECKED, checked,
14963                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14964                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14965                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14966                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14967                       GDI_DIRECT_DRAW, FALSE,
14968                       GDI_EVENT_MASK, event_mask,
14969                       GDI_CALLBACK_ACTION, HandleGameButtons,
14970                       GDI_END);
14971
14972     if (gi == NULL)
14973       Error(ERR_EXIT, "cannot create gadget");
14974
14975     game_gadget[id] = gi;
14976   }
14977 }
14978
14979 void FreeGameButtons()
14980 {
14981   int i;
14982
14983   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14984     FreeGadget(game_gadget[i]);
14985 }
14986
14987 static void MapGameButtonsAtSamePosition(int id)
14988 {
14989   int i;
14990
14991   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14992     if (i != id &&
14993         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14994         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14995       MapGadget(game_gadget[i]);
14996 }
14997
14998 static void UnmapGameButtonsAtSamePosition(int id)
14999 {
15000   int i;
15001
15002   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15003     if (i != id &&
15004         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15005         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15006       UnmapGadget(game_gadget[i]);
15007 }
15008
15009 void MapUndoRedoButtons()
15010 {
15011   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15012   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15013   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15014
15015   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15016   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15017   MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
15018 }
15019
15020 void UnmapUndoRedoButtons()
15021 {
15022   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15023   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15024   UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
15025
15026   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15027   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15028   MapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15029 }
15030
15031 void MapGameButtons()
15032 {
15033   int i;
15034
15035   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15036     if (i != GAME_CTRL_ID_UNDO &&
15037         i != GAME_CTRL_ID_REDO &&
15038         i != GAME_CTRL_ID_PLAY)
15039       MapGadget(game_gadget[i]);
15040 }
15041
15042 void UnmapGameButtons()
15043 {
15044   int i;
15045
15046   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15047     UnmapGadget(game_gadget[i]);
15048 }
15049
15050 void RedrawGameButtons()
15051 {
15052   int i;
15053
15054   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15055     RedrawGadget(game_gadget[i]);
15056
15057   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15058   redraw_mask &= ~REDRAW_ALL;
15059 }
15060
15061 void GameUndoRedoExt()
15062 {
15063   ClearPlayerAction();
15064
15065   tape.pausing = TRUE;
15066
15067   RedrawPlayfield();
15068   UpdateAndDisplayGameControlValues();
15069
15070   DrawCompleteVideoDisplay();
15071   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15072   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15073   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15074                     VIDEO_STATE_1STEP_OFF), 0);
15075
15076   BackToFront();
15077 }
15078
15079 void GameUndo(int steps)
15080 {
15081   if (!CheckEngineSnapshotList())
15082     return;
15083
15084   LoadEngineSnapshot_Undo(steps);
15085
15086   GameUndoRedoExt();
15087 }
15088
15089 void GameRedo(int steps)
15090 {
15091   if (!CheckEngineSnapshotList())
15092     return;
15093
15094   LoadEngineSnapshot_Redo(steps);
15095
15096   GameUndoRedoExt();
15097 }
15098
15099 static void HandleGameButtonsExt(int id, int button)
15100 {
15101   int steps = BUTTON_STEPSIZE(button);
15102   boolean handle_game_buttons =
15103     (game_status == GAME_MODE_PLAYING ||
15104      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15105
15106   if (!handle_game_buttons)
15107     return;
15108
15109   switch (id)
15110   {
15111     case GAME_CTRL_ID_STOP:
15112       if (game_status == GAME_MODE_MAIN)
15113         break;
15114
15115       if (tape.playing)
15116         TapeStop();
15117       else
15118         RequestQuitGame(TRUE);
15119
15120       break;
15121
15122     case GAME_CTRL_ID_PAUSE:
15123       if (options.network && game_status == GAME_MODE_PLAYING)
15124       {
15125 #if defined(NETWORK_AVALIABLE)
15126         if (tape.pausing)
15127           SendToServer_ContinuePlaying();
15128         else
15129           SendToServer_PausePlaying();
15130 #endif
15131       }
15132       else
15133         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15134       break;
15135
15136     case GAME_CTRL_ID_PLAY:
15137       if (game_status == GAME_MODE_MAIN)
15138       {
15139         StartGameActions(options.network, setup.autorecord, level.random_seed);
15140       }
15141       else if (tape.pausing)
15142       {
15143 #if defined(NETWORK_AVALIABLE)
15144         if (options.network)
15145           SendToServer_ContinuePlaying();
15146         else
15147 #endif
15148           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15149       }
15150       break;
15151
15152     case GAME_CTRL_ID_UNDO:
15153       GameUndo(steps);
15154       break;
15155
15156     case GAME_CTRL_ID_REDO:
15157       GameRedo(steps);
15158       break;
15159
15160     case GAME_CTRL_ID_SAVE:
15161       TapeQuickSave();
15162       break;
15163
15164     case GAME_CTRL_ID_LOAD:
15165       TapeQuickLoad();
15166       break;
15167
15168     case SOUND_CTRL_ID_MUSIC:
15169       if (setup.sound_music)
15170       { 
15171         setup.sound_music = FALSE;
15172
15173         FadeMusic();
15174       }
15175       else if (audio.music_available)
15176       { 
15177         setup.sound = setup.sound_music = TRUE;
15178
15179         SetAudioMode(setup.sound);
15180
15181         PlayLevelMusic();
15182       }
15183       break;
15184
15185     case SOUND_CTRL_ID_LOOPS:
15186       if (setup.sound_loops)
15187         setup.sound_loops = FALSE;
15188       else if (audio.loops_available)
15189       {
15190         setup.sound = setup.sound_loops = TRUE;
15191
15192         SetAudioMode(setup.sound);
15193       }
15194       break;
15195
15196     case SOUND_CTRL_ID_SIMPLE:
15197       if (setup.sound_simple)
15198         setup.sound_simple = FALSE;
15199       else if (audio.sound_available)
15200       {
15201         setup.sound = setup.sound_simple = TRUE;
15202
15203         SetAudioMode(setup.sound);
15204       }
15205       break;
15206
15207     default:
15208       break;
15209   }
15210 }
15211
15212 static void HandleGameButtons(struct GadgetInfo *gi)
15213 {
15214   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15215 }
15216
15217 void HandleSoundButtonKeys(Key key)
15218 {
15219
15220   if (key == setup.shortcut.sound_simple)
15221     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15222   else if (key == setup.shortcut.sound_loops)
15223     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15224   else if (key == setup.shortcut.sound_music)
15225     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15226 }