1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back!               *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment                      *
5 *               Holger Schemel                             *
6 *               Detmolder Strasse 189                      *
7 *               33604 Bielefeld                            *
8 *               Germany                                    *
9 *               e-mail: info@artsoft.org                   *
10 *----------------------------------------------------------*
11 * game.c                                                   *
12 ***********************************************************/
13 
14 #include "libgame/libgame.h"
15 
16 #include "game.h"
17 #include "init.h"
18 #include "tools.h"
19 #include "screens.h"
20 #include "files.h"
21 #include "tape.h"
22 #include "network.h"
23 
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE	FALSE
26 
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF			(			  1)
29 
30 #define USE_NEW_SP_SLIPPERY		(USE_NEW_STUFF		* 1)
31 #define USE_NEW_CUSTOM_VALUE		(USE_NEW_STUFF		* 1)
32 #define USE_NEW_PLAYER_ANIM		(USE_NEW_STUFF		* 1)
33 #define USE_NEW_ALL_SLIPPERY		(USE_NEW_STUFF		* 1)
34 #define USE_NEW_PLAYER_SPEED		(USE_NEW_STUFF		* 1)
35 #define USE_NEW_DELAYED_ACTION		(USE_NEW_STUFF		* 1)
36 #define USE_NEW_SNAP_DELAY		(USE_NEW_STUFF		* 1)
37 #define USE_ONLY_ONE_CHANGE_PER_FRAME	(USE_NEW_STUFF		* 1)
38 #define USE_ONE_MORE_CHANGE_PER_FRAME	(USE_NEW_STUFF		* 1)
39 #define USE_FIXED_DONT_RUN_INTO		(USE_NEW_STUFF		* 1)
40 #define USE_NEW_SPRING_BUMPER		(USE_NEW_STUFF		* 1)
41 #define USE_STOP_CHANGED_ELEMENTS	(USE_NEW_STUFF		* 1)
42 #define USE_ELEMENT_TOUCHING_BUGFIX	(USE_NEW_STUFF		* 1)
43 #define USE_NEW_CONTINUOUS_SNAPPING	(USE_NEW_STUFF		* 1)
44 #define USE_GFX_RESET_GFX_ANIMATION	(USE_NEW_STUFF		* 1)
45 #define USE_BOTH_SWITCHGATE_SWITCHES	(USE_NEW_STUFF		* 1)
46 #define USE_PLAYER_GRAVITY		(USE_NEW_STUFF		* 1)
47 #define USE_FIXED_BORDER_RUNNING_GFX	(USE_NEW_STUFF		* 1)
48 #define USE_QUICKSAND_BD_ROCK_BUGFIX	(USE_NEW_STUFF		* 0)
49 
50 #define USE_QUICKSAND_IMPACT_BUGFIX	(USE_NEW_STUFF		* 0)
51 
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE	(USE_NEW_STUFF		* 1)
53 
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX	(USE_NEW_STUFF		* 1)
55 
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING	(USE_NEW_STUFF		* 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK	(USE_NEW_STUFF		* 1)
58 
59 #define USE_FIX_KILLED_BY_NON_WALKABLE	(USE_NEW_STUFF		* 1)
60 #define USE_FIX_IMPACT_COLLISION	(USE_NEW_STUFF		* 1)
61 #define USE_FIX_CE_ACTION_WITH_PLAYER	(USE_NEW_STUFF		* 1)
62 #define USE_FIX_NO_ACTION_AFTER_CHANGE	(USE_NEW_STUFF		* 1)
63 
64 #define USE_PLAYER_REANIMATION		(USE_NEW_STUFF		* 1)
65 
66 #define USE_GFX_RESET_WHEN_NOT_MOVING	(USE_NEW_STUFF		* 1)
67 
68 #define USE_NEW_PLAYER_ASSIGNMENTS	(USE_NEW_STUFF		* 1)
69 
70 #define USE_DELAYED_GFX_REDRAW		(USE_NEW_STUFF		* 0)
71 
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y)				\
74 	GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbled(x, y)			\
76 	GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)		\
78 	GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
79 #define TEST_DrawTwinkleOnField(x, y)				\
80 	GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
81 #else
82 #define TEST_DrawLevelField(x, y)				\
83 	     DrawLevelField(x, y)
84 #define TEST_DrawLevelFieldCrumbled(x, y)			\
85 	     DrawLevelFieldCrumbled(x, y)
86 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)		\
87 	     DrawLevelFieldCrumbledNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y)				\
89 	     DrawTwinkleOnField(x, y)
90 #endif
91 
92 
93 /* for DigField() */
94 #define DF_NO_PUSH		0
95 #define DF_DIG			1
96 #define DF_SNAP			2
97 
98 /* for MovePlayer() */
99 #define MP_NO_ACTION		0
100 #define MP_MOVING		1
101 #define MP_ACTION		2
102 #define MP_DONT_RUN_INTO	(MP_MOVING | MP_ACTION)
103 
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT		0
106 #define SCROLL_GO_ON		1
107 
108 /* for Bang()/Explode() */
109 #define EX_PHASE_START		0
110 #define EX_TYPE_NONE		0
111 #define EX_TYPE_NORMAL		(1 << 0)
112 #define EX_TYPE_CENTER		(1 << 1)
113 #define EX_TYPE_BORDER		(1 << 2)
114 #define EX_TYPE_CROSS		(1 << 3)
115 #define EX_TYPE_DYNA		(1 << 4)
116 #define EX_TYPE_SINGLE_TILE	(EX_TYPE_CENTER | EX_TYPE_BORDER)
117 
118 #define PANEL_OFF()		(local_player->LevelSolved_PanelOff)
119 #define	PANEL_DEACTIVATED(p)	((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
120 #define PANEL_XPOS(p)		(DX + ALIGNED_TEXT_XPOS(p))
121 #define PANEL_YPOS(p)		(DY + ALIGNED_TEXT_YPOS(p))
122 
123 /* game panel display and control definitions */
124 #define GAME_PANEL_LEVEL_NUMBER			0
125 #define GAME_PANEL_GEMS				1
126 #define GAME_PANEL_INVENTORY_COUNT		2
127 #define GAME_PANEL_INVENTORY_FIRST_1		3
128 #define GAME_PANEL_INVENTORY_FIRST_2		4
129 #define GAME_PANEL_INVENTORY_FIRST_3		5
130 #define GAME_PANEL_INVENTORY_FIRST_4		6
131 #define GAME_PANEL_INVENTORY_FIRST_5		7
132 #define GAME_PANEL_INVENTORY_FIRST_6		8
133 #define GAME_PANEL_INVENTORY_FIRST_7		9
134 #define GAME_PANEL_INVENTORY_FIRST_8		10
135 #define GAME_PANEL_INVENTORY_LAST_1		11
136 #define GAME_PANEL_INVENTORY_LAST_2		12
137 #define GAME_PANEL_INVENTORY_LAST_3		13
138 #define GAME_PANEL_INVENTORY_LAST_4		14
139 #define GAME_PANEL_INVENTORY_LAST_5		15
140 #define GAME_PANEL_INVENTORY_LAST_6		16
141 #define GAME_PANEL_INVENTORY_LAST_7		17
142 #define GAME_PANEL_INVENTORY_LAST_8		18
143 #define GAME_PANEL_KEY_1			19
144 #define GAME_PANEL_KEY_2			20
145 #define GAME_PANEL_KEY_3			21
146 #define GAME_PANEL_KEY_4			22
147 #define GAME_PANEL_KEY_5			23
148 #define GAME_PANEL_KEY_6			24
149 #define GAME_PANEL_KEY_7			25
150 #define GAME_PANEL_KEY_8			26
151 #define GAME_PANEL_KEY_WHITE			27
152 #define GAME_PANEL_KEY_WHITE_COUNT		28
153 #define GAME_PANEL_SCORE			29
154 #define GAME_PANEL_HIGHSCORE			30
155 #define GAME_PANEL_TIME				31
156 #define GAME_PANEL_TIME_HH			32
157 #define GAME_PANEL_TIME_MM			33
158 #define GAME_PANEL_TIME_SS			34
159 #define GAME_PANEL_FRAME			35
160 #define GAME_PANEL_SHIELD_NORMAL		36
161 #define GAME_PANEL_SHIELD_NORMAL_TIME		37
162 #define GAME_PANEL_SHIELD_DEADLY		38
163 #define GAME_PANEL_SHIELD_DEADLY_TIME		39
164 #define GAME_PANEL_EXIT				40
165 #define GAME_PANEL_EMC_MAGIC_BALL		41
166 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH	42
167 #define GAME_PANEL_LIGHT_SWITCH			43
168 #define GAME_PANEL_LIGHT_SWITCH_TIME		44
169 #define GAME_PANEL_TIMEGATE_SWITCH		45
170 #define GAME_PANEL_TIMEGATE_SWITCH_TIME		46
171 #define GAME_PANEL_SWITCHGATE_SWITCH		47
172 #define GAME_PANEL_EMC_LENSES			48
173 #define GAME_PANEL_EMC_LENSES_TIME		49
174 #define GAME_PANEL_EMC_MAGNIFIER		50
175 #define GAME_PANEL_EMC_MAGNIFIER_TIME		51
176 #define GAME_PANEL_BALLOON_SWITCH		52
177 #define GAME_PANEL_DYNABOMB_NUMBER		53
178 #define GAME_PANEL_DYNABOMB_SIZE		54
179 #define GAME_PANEL_DYNABOMB_POWER		55
180 #define GAME_PANEL_PENGUINS			56
181 #define GAME_PANEL_SOKOBAN_OBJECTS		57
182 #define GAME_PANEL_SOKOBAN_FIELDS		58
183 #define GAME_PANEL_ROBOT_WHEEL			59
184 #define GAME_PANEL_CONVEYOR_BELT_1		60
185 #define GAME_PANEL_CONVEYOR_BELT_2		61
186 #define GAME_PANEL_CONVEYOR_BELT_3		62
187 #define GAME_PANEL_CONVEYOR_BELT_4		63
188 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH	64
189 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH	65
190 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH	66
191 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH	67
192 #define GAME_PANEL_MAGIC_WALL			68
193 #define GAME_PANEL_MAGIC_WALL_TIME		69
194 #define GAME_PANEL_GRAVITY_STATE		70
195 #define GAME_PANEL_GRAPHIC_1			71
196 #define GAME_PANEL_GRAPHIC_2			72
197 #define GAME_PANEL_GRAPHIC_3			73
198 #define GAME_PANEL_GRAPHIC_4			74
199 #define GAME_PANEL_GRAPHIC_5			75
200 #define GAME_PANEL_GRAPHIC_6			76
201 #define GAME_PANEL_GRAPHIC_7			77
202 #define GAME_PANEL_GRAPHIC_8			78
203 #define GAME_PANEL_ELEMENT_1			79
204 #define GAME_PANEL_ELEMENT_2			80
205 #define GAME_PANEL_ELEMENT_3			81
206 #define GAME_PANEL_ELEMENT_4			82
207 #define GAME_PANEL_ELEMENT_5			83
208 #define GAME_PANEL_ELEMENT_6			84
209 #define GAME_PANEL_ELEMENT_7			85
210 #define GAME_PANEL_ELEMENT_8			86
211 #define GAME_PANEL_ELEMENT_COUNT_1		87
212 #define GAME_PANEL_ELEMENT_COUNT_2		88
213 #define GAME_PANEL_ELEMENT_COUNT_3		89
214 #define GAME_PANEL_ELEMENT_COUNT_4		90
215 #define GAME_PANEL_ELEMENT_COUNT_5		91
216 #define GAME_PANEL_ELEMENT_COUNT_6		92
217 #define GAME_PANEL_ELEMENT_COUNT_7		93
218 #define GAME_PANEL_ELEMENT_COUNT_8		94
219 #define GAME_PANEL_CE_SCORE_1			95
220 #define GAME_PANEL_CE_SCORE_2			96
221 #define GAME_PANEL_CE_SCORE_3			97
222 #define GAME_PANEL_CE_SCORE_4			98
223 #define GAME_PANEL_CE_SCORE_5			99
224 #define GAME_PANEL_CE_SCORE_6			100
225 #define GAME_PANEL_CE_SCORE_7			101
226 #define GAME_PANEL_CE_SCORE_8			102
227 #define GAME_PANEL_CE_SCORE_1_ELEMENT		103
228 #define GAME_PANEL_CE_SCORE_2_ELEMENT		104
229 #define GAME_PANEL_CE_SCORE_3_ELEMENT		105
230 #define GAME_PANEL_CE_SCORE_4_ELEMENT		106
231 #define GAME_PANEL_CE_SCORE_5_ELEMENT		107
232 #define GAME_PANEL_CE_SCORE_6_ELEMENT		108
233 #define GAME_PANEL_CE_SCORE_7_ELEMENT		109
234 #define GAME_PANEL_CE_SCORE_8_ELEMENT		110
235 #define GAME_PANEL_PLAYER_NAME			111
236 #define GAME_PANEL_LEVEL_NAME			112
237 #define GAME_PANEL_LEVEL_AUTHOR			113
238 
239 #define NUM_GAME_PANEL_CONTROLS			114
240 
241 struct GamePanelOrderInfo
242 {
243   int nr;
244   int sort_priority;
245 };
246 
247 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
248 
249 struct GamePanelControlInfo
250 {
251   int nr;
252 
253   struct TextPosInfo *pos;
254   int type;
255 
256   int value, last_value;
257   int frame, last_frame;
258   int gfx_frame;
259   int gfx_random;
260 };
261 
262 static struct GamePanelControlInfo game_panel_controls[] =
263 {
264   {
265     GAME_PANEL_LEVEL_NUMBER,
266     &game.panel.level_number,
267     TYPE_INTEGER,
268   },
269   {
270     GAME_PANEL_GEMS,
271     &game.panel.gems,
272     TYPE_INTEGER,
273   },
274   {
275     GAME_PANEL_INVENTORY_COUNT,
276     &game.panel.inventory_count,
277     TYPE_INTEGER,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_1,
281     &game.panel.inventory_first[0],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_FIRST_2,
286     &game.panel.inventory_first[1],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_FIRST_3,
291     &game.panel.inventory_first[2],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_FIRST_4,
296     &game.panel.inventory_first[3],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_FIRST_5,
301     &game.panel.inventory_first[4],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_FIRST_6,
306     &game.panel.inventory_first[5],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_FIRST_7,
311     &game.panel.inventory_first[6],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_FIRST_8,
316     &game.panel.inventory_first[7],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_1,
321     &game.panel.inventory_last[0],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_INVENTORY_LAST_2,
326     &game.panel.inventory_last[1],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_INVENTORY_LAST_3,
331     &game.panel.inventory_last[2],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_INVENTORY_LAST_4,
336     &game.panel.inventory_last[3],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_INVENTORY_LAST_5,
341     &game.panel.inventory_last[4],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_INVENTORY_LAST_6,
346     &game.panel.inventory_last[5],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_INVENTORY_LAST_7,
351     &game.panel.inventory_last[6],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_INVENTORY_LAST_8,
356     &game.panel.inventory_last[7],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_1,
361     &game.panel.key[0],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_2,
366     &game.panel.key[1],
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_3,
371     &game.panel.key[2],
372     TYPE_ELEMENT,
373   },
374   {
375     GAME_PANEL_KEY_4,
376     &game.panel.key[3],
377     TYPE_ELEMENT,
378   },
379   {
380     GAME_PANEL_KEY_5,
381     &game.panel.key[4],
382     TYPE_ELEMENT,
383   },
384   {
385     GAME_PANEL_KEY_6,
386     &game.panel.key[5],
387     TYPE_ELEMENT,
388   },
389   {
390     GAME_PANEL_KEY_7,
391     &game.panel.key[6],
392     TYPE_ELEMENT,
393   },
394   {
395     GAME_PANEL_KEY_8,
396     &game.panel.key[7],
397     TYPE_ELEMENT,
398   },
399   {
400     GAME_PANEL_KEY_WHITE,
401     &game.panel.key_white,
402     TYPE_ELEMENT,
403   },
404   {
405     GAME_PANEL_KEY_WHITE_COUNT,
406     &game.panel.key_white_count,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SCORE,
411     &game.panel.score,
412     TYPE_INTEGER,
413   },
414   {
415     GAME_PANEL_HIGHSCORE,
416     &game.panel.highscore,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_TIME,
421     &game.panel.time,
422     TYPE_INTEGER,
423   },
424   {
425     GAME_PANEL_TIME_HH,
426     &game.panel.time_hh,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_TIME_MM,
431     &game.panel.time_mm,
432     TYPE_INTEGER,
433   },
434   {
435     GAME_PANEL_TIME_SS,
436     &game.panel.time_ss,
437     TYPE_INTEGER,
438   },
439   {
440     GAME_PANEL_FRAME,
441     &game.panel.frame,
442     TYPE_INTEGER,
443   },
444   {
445     GAME_PANEL_SHIELD_NORMAL,
446     &game.panel.shield_normal,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_SHIELD_NORMAL_TIME,
451     &game.panel.shield_normal_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_DEADLY,
456     &game.panel.shield_deadly,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_DEADLY_TIME,
461     &game.panel.shield_deadly_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_EXIT,
466     &game.panel.exit,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_MAGIC_BALL,
471     &game.panel.emc_magic_ball,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
476     &game.panel.emc_magic_ball_switch,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_LIGHT_SWITCH,
481     &game.panel.light_switch,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_LIGHT_SWITCH_TIME,
486     &game.panel.light_switch_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_TIMEGATE_SWITCH,
491     &game.panel.timegate_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_TIMEGATE_SWITCH_TIME,
496     &game.panel.timegate_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_SWITCHGATE_SWITCH,
501     &game.panel.switchgate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_EMC_LENSES,
506     &game.panel.emc_lenses,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_EMC_LENSES_TIME,
511     &game.panel.emc_lenses_time,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_EMC_MAGNIFIER,
516     &game.panel.emc_magnifier,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_MAGNIFIER_TIME,
521     &game.panel.emc_magnifier_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_BALLOON_SWITCH,
526     &game.panel.balloon_switch,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_DYNABOMB_NUMBER,
531     &game.panel.dynabomb_number,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_DYNABOMB_SIZE,
536     &game.panel.dynabomb_size,
537     TYPE_INTEGER,
538   },
539   {
540     GAME_PANEL_DYNABOMB_POWER,
541     &game.panel.dynabomb_power,
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_PENGUINS,
546     &game.panel.penguins,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_SOKOBAN_OBJECTS,
551     &game.panel.sokoban_objects,
552     TYPE_INTEGER,
553   },
554   {
555     GAME_PANEL_SOKOBAN_FIELDS,
556     &game.panel.sokoban_fields,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_ROBOT_WHEEL,
561     &game.panel.robot_wheel,
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_1,
566     &game.panel.conveyor_belt[0],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_CONVEYOR_BELT_2,
571     &game.panel.conveyor_belt[1],
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_3,
576     &game.panel.conveyor_belt[2],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_4,
581     &game.panel.conveyor_belt[3],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
586     &game.panel.conveyor_belt_switch[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
591     &game.panel.conveyor_belt_switch[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
596     &game.panel.conveyor_belt_switch[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
601     &game.panel.conveyor_belt_switch[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_MAGIC_WALL,
606     &game.panel.magic_wall,
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_MAGIC_WALL_TIME,
611     &game.panel.magic_wall_time,
612     TYPE_INTEGER,
613   },
614   {
615     GAME_PANEL_GRAVITY_STATE,
616     &game.panel.gravity_state,
617     TYPE_STRING,
618   },
619   {
620     GAME_PANEL_GRAPHIC_1,
621     &game.panel.graphic[0],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_GRAPHIC_2,
626     &game.panel.graphic[1],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_GRAPHIC_3,
631     &game.panel.graphic[2],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_4,
636     &game.panel.graphic[3],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_5,
641     &game.panel.graphic[4],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_6,
646     &game.panel.graphic[5],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_7,
651     &game.panel.graphic[6],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_8,
656     &game.panel.graphic[7],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_1,
661     &game.panel.element[0],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_2,
666     &game.panel.element[1],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_3,
671     &game.panel.element[2],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_4,
676     &game.panel.element[3],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_5,
681     &game.panel.element[4],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_6,
686     &game.panel.element[5],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_7,
691     &game.panel.element[6],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_8,
696     &game.panel.element[7],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_1,
701     &game.panel.element_count[0],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_ELEMENT_COUNT_2,
706     &game.panel.element_count[1],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_3,
711     &game.panel.element_count[2],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_4,
716     &game.panel.element_count[3],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_5,
721     &game.panel.element_count[4],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_6,
726     &game.panel.element_count[5],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_7,
731     &game.panel.element_count[6],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_8,
736     &game.panel.element_count[7],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_1,
741     &game.panel.ce_score[0],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_2,
746     &game.panel.ce_score[1],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_3,
751     &game.panel.ce_score[2],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_4,
756     &game.panel.ce_score[3],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_5,
761     &game.panel.ce_score[4],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_6,
766     &game.panel.ce_score[5],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_7,
771     &game.panel.ce_score[6],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_8,
776     &game.panel.ce_score[7],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_1_ELEMENT,
781     &game.panel.ce_score_element[0],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_CE_SCORE_2_ELEMENT,
786     &game.panel.ce_score_element[1],
787     TYPE_ELEMENT,
788   },
789   {
790     GAME_PANEL_CE_SCORE_3_ELEMENT,
791     &game.panel.ce_score_element[2],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_4_ELEMENT,
796     &game.panel.ce_score_element[3],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_5_ELEMENT,
801     &game.panel.ce_score_element[4],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_6_ELEMENT,
806     &game.panel.ce_score_element[5],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_7_ELEMENT,
811     &game.panel.ce_score_element[6],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_8_ELEMENT,
816     &game.panel.ce_score_element[7],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_PLAYER_NAME,
821     &game.panel.player_name,
822     TYPE_STRING,
823   },
824   {
825     GAME_PANEL_LEVEL_NAME,
826     &game.panel.level_name,
827     TYPE_STRING,
828   },
829   {
830     GAME_PANEL_LEVEL_AUTHOR,
831     &game.panel.level_author,
832     TYPE_STRING,
833   },
834 
835   {
836     -1,
837     NULL,
838     -1,
839   }
840 };
841 
842 /* values for delayed check of falling and moving elements and for collision */
843 #define CHECK_DELAY_MOVING	3
844 #define CHECK_DELAY_FALLING	CHECK_DELAY_MOVING
845 #define CHECK_DELAY_COLLISION	2
846 #define CHECK_DELAY_IMPACT	CHECK_DELAY_COLLISION
847 
848 /* values for initial player move delay (initial delay counter value) */
849 #define INITIAL_MOVE_DELAY_OFF	-1
850 #define INITIAL_MOVE_DELAY_ON	0
851 
852 /* values for player movement speed (which is in fact a delay value) */
853 #define MOVE_DELAY_MIN_SPEED	32
854 #define MOVE_DELAY_NORMAL_SPEED	8
855 #define MOVE_DELAY_HIGH_SPEED	4
856 #define MOVE_DELAY_MAX_SPEED	1
857 
858 #define DOUBLE_MOVE_DELAY(x)	(x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
859 #define HALVE_MOVE_DELAY(x)	(x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
860 
861 #define DOUBLE_PLAYER_SPEED(p)	(HALVE_MOVE_DELAY( (p)->move_delay_value))
862 #define HALVE_PLAYER_SPEED(p)	(DOUBLE_MOVE_DELAY((p)->move_delay_value))
863 
864 /* values for other actions */
865 #define MOVE_STEPSIZE_NORMAL	(TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN	(1)
867 #define MOVE_STEPSIZE_MAX	(TILEX)
868 
869 #define GET_DX_FROM_DIR(d)	((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)	((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871 
872 #define	INIT_GFX_RANDOM()	(GetSimpleRandom(1000000))
873 
874 #define GET_NEW_PUSH_DELAY(e)	(   (element_info[e].push_delay_fixed) + \
875 				 RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)	(   (element_info[e].drop_delay_fixed) + \
877 				 RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)	(   (element_info[e].move_delay_fixed) + \
879 				 RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)	(   (element_info[e].move_delay_fixed) + \
881 				    (element_info[e].move_delay_random))
882 #define GET_NEW_CE_VALUE(e)	(   (element_info[e].ce_value_fixed_initial) +\
883 				 RND(element_info[e].ce_value_random_initial))
884 #define GET_CE_SCORE(e)		(   (element_info[e].collect_score))
885 #define GET_CHANGE_DELAY(c)	(   ((c)->delay_fixed  * (c)->delay_frames) + \
886 				 RND((c)->delay_random * (c)->delay_frames))
887 #define GET_CE_DELAY_VALUE(c)	(   ((c)->delay_fixed) + \
888 				 RND((c)->delay_random))
889 
890 
891 #define GET_VALID_RUNTIME_ELEMENT(e)					\
892 	 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
893 
894 #define RESOLVED_REFERENCE_ELEMENT(be, e)				\
895 	((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :	\
896 	 (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :	\
897 	 (be) + (e) - EL_SELF)
898 
899 #define GET_PLAYER_FROM_BITS(p)						\
900 	(EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
901 
902 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)				\
903 	((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :	\
904 	 (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :	\
905 	 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :	\
906 	 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :	\
907 	 (e) == EL_CURRENT_CE_VALUE ? (cv) :				\
908 	 (e) == EL_CURRENT_CE_SCORE ? (cs) :				\
909 	 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?			\
910 	 RESOLVED_REFERENCE_ELEMENT(be, e) :				\
911 	 (e))
912 
913 #define CAN_GROW_INTO(e)						\
914 	((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
915 
916 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)			\
917 		(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||		\
918 					(condition)))
919 
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)		\
921 		(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||		\
922 					(CAN_MOVE_INTO_ACID(e) &&	\
923 					 Feld[x][y] == EL_ACID) ||	\
924 					(condition)))
925 
926 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)		\
927 		(IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||	\
928 					(CAN_MOVE_INTO_ACID(e) &&	\
929 					 Feld[x][y] == EL_ACID) ||	\
930 					(condition)))
931 
932 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)		\
933 		(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||		\
934 					(condition) ||			\
935 					(CAN_MOVE_INTO_ACID(e) &&	\
936 					 Feld[x][y] == EL_ACID) ||	\
937 					(DONT_COLLIDE_WITH(e) &&	\
938 					 IS_PLAYER(x, y) &&		\
939 					 !PLAYER_ENEMY_PROTECTED(x, y))))
940 
941 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)				\
942 	ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
943 
944 #define SATELLITE_CAN_ENTER_FIELD(x, y)					\
945 	ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
946 
947 #define ANDROID_CAN_ENTER_FIELD(e, x, y)				\
948 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
949 
950 #define ANDROID_CAN_CLONE_FIELD(x, y)					\
951 	(IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) ||	\
952 				CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
953 
954 #define ENEMY_CAN_ENTER_FIELD(e, x, y)					\
955 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
956 
957 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)					\
958 	ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
959 
960 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)				\
961 	ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
962 
963 #define PACMAN_CAN_ENTER_FIELD(e, x, y)					\
964 	ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
965 
966 #define PIG_CAN_ENTER_FIELD(e, x, y)					\
967 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
968 
969 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)				\
970 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
971 						 Feld[x][y] == EL_EM_EXIT_OPEN || \
972 						 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
973 						 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
974 						 IS_FOOD_PENGUIN(Feld[x][y])))
975 #define DRAGON_CAN_ENTER_FIELD(e, x, y)					\
976 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
977 
978 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)			\
979 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
980 
981 #define SPRING_CAN_ENTER_FIELD(e, x, y)					\
982 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
983 
984 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)				\
985 	(IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||	\
986 				Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
987 
988 #define MOVE_ENTER_EL(e)	(element_info[e].move_enter_element)
989 
990 #define CE_ENTER_FIELD_COND(e, x, y)					\
991 		(!IS_PLAYER(x, y) &&					\
992 		 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
993 
994 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)				\
995 	ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
996 
997 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
998 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
999 
1000 #define ACCESS_FROM(e, d)		(element_info[e].access_direction &(d))
1001 #define IS_WALKABLE_FROM(e, d)		(IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1002 #define IS_PASSABLE_FROM(e, d)		(IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1003 #define IS_ACCESSIBLE_FROM(e, d)	(IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1004 
1005 /* game button identifiers */
1006 #define GAME_CTRL_ID_STOP		0
1007 #define GAME_CTRL_ID_PAUSE		1
1008 #define GAME_CTRL_ID_PLAY		2
1009 #define SOUND_CTRL_ID_MUSIC		3
1010 #define SOUND_CTRL_ID_LOOPS		4
1011 #define SOUND_CTRL_ID_SIMPLE		5
1012 
1013 #define NUM_GAME_BUTTONS		6
1014 
1015 
1016 /* forward declaration for internal use */
1017 
1018 static void CreateField(int, int, int);
1019 
1020 static void ResetGfxAnimation(int, int);
1021 
1022 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1023 static void AdvanceFrameAndPlayerCounters(int);
1024 
1025 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1026 static boolean MovePlayer(struct PlayerInfo *, int, int);
1027 static void ScrollPlayer(struct PlayerInfo *, int);
1028 static void ScrollScreen(struct PlayerInfo *, int);
1029 
1030 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1031 static boolean DigFieldByCE(int, int, int);
1032 static boolean SnapField(struct PlayerInfo *, int, int);
1033 static boolean DropElement(struct PlayerInfo *);
1034 
1035 static void InitBeltMovement(void);
1036 static void CloseAllOpenTimegates(void);
1037 static void CheckGravityMovement(struct PlayerInfo *);
1038 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1039 static void KillPlayerUnlessEnemyProtected(int, int);
1040 static void KillPlayerUnlessExplosionProtected(int, int);
1041 
1042 static void TestIfPlayerTouchesCustomElement(int, int);
1043 static void TestIfElementTouchesCustomElement(int, int);
1044 static void TestIfElementHitsCustomElement(int, int, int);
1045 #if 0
1046 static void TestIfElementSmashesCustomElement(int, int, int);
1047 #endif
1048 
1049 static void HandleElementChange(int, int, int);
1050 static void ExecuteCustomElementAction(int, int, int, int);
1051 static boolean ChangeElement(int, int, int, int);
1052 
1053 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1054 #define CheckTriggeredElementChange(x, y, e, ev)			\
1055 	CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1056 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)		\
1057 	CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1058 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)		\
1059 	CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1060 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)		\
1061 	CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1062 
1063 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1064 #define CheckElementChange(x, y, e, te, ev)				\
1065 	CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1066 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)			\
1067 	CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1068 #define CheckElementChangeBySide(x, y, e, te, ev, s)			\
1069 	CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1070 
1071 static void PlayLevelSound(int, int, int);
1072 static void PlayLevelSoundNearest(int, int, int);
1073 static void PlayLevelSoundAction(int, int, int);
1074 static void PlayLevelSoundElementAction(int, int, int, int);
1075 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1076 static void PlayLevelSoundActionIfLoop(int, int, int);
1077 static void StopLevelSoundActionIfLoop(int, int, int);
1078 static void PlayLevelMusic();
1079 
1080 static void MapGameButtons();
1081 static void HandleGameButtons(struct GadgetInfo *);
1082 
1083 int AmoebeNachbarNr(int, int);
1084 void AmoebeUmwandeln(int, int);
1085 void ContinueMoving(int, int);
1086 void Bang(int, int);
1087 void InitMovDir(int, int);
1088 void InitAmoebaNr(int, int);
1089 int NewHiScore(void);
1090 
1091 void TestIfGoodThingHitsBadThing(int, int, int);
1092 void TestIfBadThingHitsGoodThing(int, int, int);
1093 void TestIfPlayerTouchesBadThing(int, int);
1094 void TestIfPlayerRunsIntoBadThing(int, int, int);
1095 void TestIfBadThingTouchesPlayer(int, int);
1096 void TestIfBadThingRunsIntoPlayer(int, int, int);
1097 void TestIfFriendTouchesBadThing(int, int);
1098 void TestIfBadThingTouchesFriend(int, int);
1099 void TestIfBadThingTouchesOtherBadThing(int, int);
1100 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1101 
1102 void KillPlayer(struct PlayerInfo *);
1103 void BuryPlayer(struct PlayerInfo *);
1104 void RemovePlayer(struct PlayerInfo *);
1105 
1106 static int getInvisibleActiveFromInvisibleElement(int);
1107 static int getInvisibleFromInvisibleActiveElement(int);
1108 
1109 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1110 
1111 /* for detection of endless loops, caused by custom element programming */
1112 /* (using maximal playfield width x 10 is just a rough approximation) */
1113 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH	(MAX_PLAYFIELD_WIDTH * 10)
1114 
1115 #define RECURSION_LOOP_DETECTION_START(e, rc)				\
1116 {									\
1117   if (recursion_loop_detected)						\
1118     return (rc);							\
1119 									\
1120   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)	\
1121   {									\
1122     recursion_loop_detected = TRUE;					\
1123     recursion_loop_element = (e);					\
1124   }									\
1125 									\
1126   recursion_loop_depth++;						\
1127 }
1128 
1129 #define RECURSION_LOOP_DETECTION_END()					\
1130 {									\
1131   recursion_loop_depth--;						\
1132 }
1133 
1134 static int recursion_loop_depth;
1135 static boolean recursion_loop_detected;
1136 static boolean recursion_loop_element;
1137 
1138 static int map_player_action[MAX_PLAYERS];
1139 
1140 
1141 /* ------------------------------------------------------------------------- */
1142 /* definition of elements that automatically change to other elements after  */
1143 /* a specified time, eventually calling a function when changing             */
1144 /* ------------------------------------------------------------------------- */
1145 
1146 /* forward declaration for changer functions */
1147 static void InitBuggyBase(int, int);
1148 static void WarnBuggyBase(int, int);
1149 
1150 static void InitTrap(int, int);
1151 static void ActivateTrap(int, int);
1152 static void ChangeActiveTrap(int, int);
1153 
1154 static void InitRobotWheel(int, int);
1155 static void RunRobotWheel(int, int);
1156 static void StopRobotWheel(int, int);
1157 
1158 static void InitTimegateWheel(int, int);
1159 static void RunTimegateWheel(int, int);
1160 
1161 static void InitMagicBallDelay(int, int);
1162 static void ActivateMagicBall(int, int);
1163 
1164 struct ChangingElementInfo
1165 {
1166   int element;
1167   int target_element;
1168   int change_delay;
1169   void (*pre_change_function)(int x, int y);
1170   void (*change_function)(int x, int y);
1171   void (*post_change_function)(int x, int y);
1172 };
1173 
1174 static struct ChangingElementInfo change_delay_list[] =
1175 {
1176   {
1177     EL_NUT_BREAKING,
1178     EL_EMERALD,
1179     6,
1180     NULL,
1181     NULL,
1182     NULL
1183   },
1184   {
1185     EL_PEARL_BREAKING,
1186     EL_EMPTY,
1187     8,
1188     NULL,
1189     NULL,
1190     NULL
1191   },
1192   {
1193     EL_EXIT_OPENING,
1194     EL_EXIT_OPEN,
1195     29,
1196     NULL,
1197     NULL,
1198     NULL
1199   },
1200   {
1201     EL_EXIT_CLOSING,
1202     EL_EXIT_CLOSED,
1203     29,
1204     NULL,
1205     NULL,
1206     NULL
1207   },
1208   {
1209     EL_STEEL_EXIT_OPENING,
1210     EL_STEEL_EXIT_OPEN,
1211     29,
1212     NULL,
1213     NULL,
1214     NULL
1215   },
1216   {
1217     EL_STEEL_EXIT_CLOSING,
1218     EL_STEEL_EXIT_CLOSED,
1219     29,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_EM_EXIT_OPENING,
1226     EL_EM_EXIT_OPEN,
1227     29,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EM_EXIT_CLOSING,
1234 #if 1
1235     EL_EMPTY,
1236 #else
1237     EL_EM_EXIT_CLOSED,
1238 #endif
1239     29,
1240     NULL,
1241     NULL,
1242     NULL
1243   },
1244   {
1245     EL_EM_STEEL_EXIT_OPENING,
1246     EL_EM_STEEL_EXIT_OPEN,
1247     29,
1248     NULL,
1249     NULL,
1250     NULL
1251   },
1252   {
1253     EL_EM_STEEL_EXIT_CLOSING,
1254 #if 1
1255     EL_STEELWALL,
1256 #else
1257     EL_EM_STEEL_EXIT_CLOSED,
1258 #endif
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_SP_EXIT_OPENING,
1266     EL_SP_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_SP_EXIT_CLOSING,
1274     EL_SP_EXIT_CLOSED,
1275     29,
1276     NULL,
1277     NULL,
1278     NULL
1279   },
1280   {
1281     EL_SWITCHGATE_OPENING,
1282     EL_SWITCHGATE_OPEN,
1283     29,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_SWITCHGATE_CLOSING,
1290     EL_SWITCHGATE_CLOSED,
1291     29,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_TIMEGATE_OPENING,
1298     EL_TIMEGATE_OPEN,
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_TIMEGATE_CLOSING,
1306     EL_TIMEGATE_CLOSED,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312 
1313   {
1314     EL_ACID_SPLASH_LEFT,
1315     EL_EMPTY,
1316     8,
1317     NULL,
1318     NULL,
1319     NULL
1320   },
1321   {
1322     EL_ACID_SPLASH_RIGHT,
1323     EL_EMPTY,
1324     8,
1325     NULL,
1326     NULL,
1327     NULL
1328   },
1329   {
1330     EL_SP_BUGGY_BASE,
1331     EL_SP_BUGGY_BASE_ACTIVATING,
1332     0,
1333     InitBuggyBase,
1334     NULL,
1335     NULL
1336   },
1337   {
1338     EL_SP_BUGGY_BASE_ACTIVATING,
1339     EL_SP_BUGGY_BASE_ACTIVE,
1340     0,
1341     InitBuggyBase,
1342     NULL,
1343     NULL
1344   },
1345   {
1346     EL_SP_BUGGY_BASE_ACTIVE,
1347     EL_SP_BUGGY_BASE,
1348     0,
1349     InitBuggyBase,
1350     WarnBuggyBase,
1351     NULL
1352   },
1353   {
1354     EL_TRAP,
1355     EL_TRAP_ACTIVE,
1356     0,
1357     InitTrap,
1358     NULL,
1359     ActivateTrap
1360   },
1361   {
1362     EL_TRAP_ACTIVE,
1363     EL_TRAP,
1364     31,
1365     NULL,
1366     ChangeActiveTrap,
1367     NULL
1368   },
1369   {
1370     EL_ROBOT_WHEEL_ACTIVE,
1371     EL_ROBOT_WHEEL,
1372     0,
1373     InitRobotWheel,
1374     RunRobotWheel,
1375     StopRobotWheel
1376   },
1377   {
1378     EL_TIMEGATE_SWITCH_ACTIVE,
1379     EL_TIMEGATE_SWITCH,
1380     0,
1381     InitTimegateWheel,
1382     RunTimegateWheel,
1383     NULL
1384   },
1385   {
1386     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1387     EL_DC_TIMEGATE_SWITCH,
1388     0,
1389     InitTimegateWheel,
1390     RunTimegateWheel,
1391     NULL
1392   },
1393   {
1394     EL_EMC_MAGIC_BALL_ACTIVE,
1395     EL_EMC_MAGIC_BALL_ACTIVE,
1396     0,
1397     InitMagicBallDelay,
1398     NULL,
1399     ActivateMagicBall
1400   },
1401   {
1402     EL_EMC_SPRING_BUMPER_ACTIVE,
1403     EL_EMC_SPRING_BUMPER,
1404     8,
1405     NULL,
1406     NULL,
1407     NULL
1408   },
1409   {
1410     EL_DIAGONAL_SHRINKING,
1411     EL_UNDEFINED,
1412     0,
1413     NULL,
1414     NULL,
1415     NULL
1416   },
1417   {
1418     EL_DIAGONAL_GROWING,
1419     EL_UNDEFINED,
1420     0,
1421     NULL,
1422     NULL,
1423     NULL,
1424   },
1425 
1426   {
1427     EL_UNDEFINED,
1428     EL_UNDEFINED,
1429     -1,
1430     NULL,
1431     NULL,
1432     NULL
1433   }
1434 };
1435 
1436 struct
1437 {
1438   int element;
1439   int push_delay_fixed, push_delay_random;
1440 }
1441 push_delay_list[] =
1442 {
1443   { EL_SPRING,			0, 0 },
1444   { EL_BALLOON,			0, 0 },
1445 
1446   { EL_SOKOBAN_OBJECT,		2, 0 },
1447   { EL_SOKOBAN_FIELD_FULL,	2, 0 },
1448   { EL_SATELLITE,		2, 0 },
1449   { EL_SP_DISK_YELLOW,		2, 0 },
1450 
1451   { EL_UNDEFINED,		0, 0 },
1452 };
1453 
1454 struct
1455 {
1456   int element;
1457   int move_stepsize;
1458 }
1459 move_stepsize_list[] =
1460 {
1461   { EL_AMOEBA_DROP,		2 },
1462   { EL_AMOEBA_DROPPING,		2 },
1463   { EL_QUICKSAND_FILLING,	1 },
1464   { EL_QUICKSAND_EMPTYING,	1 },
1465   { EL_QUICKSAND_FAST_FILLING,	2 },
1466   { EL_QUICKSAND_FAST_EMPTYING,	2 },
1467   { EL_MAGIC_WALL_FILLING,	2 },
1468   { EL_MAGIC_WALL_EMPTYING,	2 },
1469   { EL_BD_MAGIC_WALL_FILLING,	2 },
1470   { EL_BD_MAGIC_WALL_EMPTYING,	2 },
1471   { EL_DC_MAGIC_WALL_FILLING,	2 },
1472   { EL_DC_MAGIC_WALL_EMPTYING,	2 },
1473 
1474   { EL_UNDEFINED,		0 },
1475 };
1476 
1477 struct
1478 {
1479   int element;
1480   int count;
1481 }
1482 collect_count_list[] =
1483 {
1484   { EL_EMERALD,			1 },
1485   { EL_BD_DIAMOND,		1 },
1486   { EL_EMERALD_YELLOW,		1 },
1487   { EL_EMERALD_RED,		1 },
1488   { EL_EMERALD_PURPLE,		1 },
1489   { EL_DIAMOND,			3 },
1490   { EL_SP_INFOTRON,		1 },
1491   { EL_PEARL,			5 },
1492   { EL_CRYSTAL,			8 },
1493 
1494   { EL_UNDEFINED,		0 },
1495 };
1496 
1497 struct
1498 {
1499   int element;
1500   int direction;
1501 }
1502 access_direction_list[] =
1503 {
1504   { EL_TUBE_ANY,			MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1505   { EL_TUBE_VERTICAL,			                     MV_UP | MV_DOWN },
1506   { EL_TUBE_HORIZONTAL,			MV_LEFT | MV_RIGHT                   },
1507   { EL_TUBE_VERTICAL_LEFT,		MV_LEFT |            MV_UP | MV_DOWN },
1508   { EL_TUBE_VERTICAL_RIGHT,		          MV_RIGHT | MV_UP | MV_DOWN },
1509   { EL_TUBE_HORIZONTAL_UP,		MV_LEFT | MV_RIGHT | MV_UP           },
1510   { EL_TUBE_HORIZONTAL_DOWN,		MV_LEFT | MV_RIGHT |         MV_DOWN },
1511   { EL_TUBE_LEFT_UP,			MV_LEFT |            MV_UP           },
1512   { EL_TUBE_LEFT_DOWN,			MV_LEFT |                    MV_DOWN },
1513   { EL_TUBE_RIGHT_UP,			          MV_RIGHT | MV_UP           },
1514   { EL_TUBE_RIGHT_DOWN,			          MV_RIGHT |         MV_DOWN },
1515 
1516   { EL_SP_PORT_LEFT,			          MV_RIGHT                   },
1517   { EL_SP_PORT_RIGHT,			MV_LEFT                              },
1518   { EL_SP_PORT_UP,			                             MV_DOWN },
1519   { EL_SP_PORT_DOWN,			                     MV_UP           },
1520   { EL_SP_PORT_HORIZONTAL,		MV_LEFT | MV_RIGHT                   },
1521   { EL_SP_PORT_VERTICAL,		                     MV_UP | MV_DOWN },
1522   { EL_SP_PORT_ANY,			MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_SP_GRAVITY_PORT_LEFT,		          MV_RIGHT                   },
1524   { EL_SP_GRAVITY_PORT_RIGHT,		MV_LEFT                              },
1525   { EL_SP_GRAVITY_PORT_UP,		                             MV_DOWN },
1526   { EL_SP_GRAVITY_PORT_DOWN,		                     MV_UP           },
1527   { EL_SP_GRAVITY_ON_PORT_LEFT,		          MV_RIGHT                   },
1528   { EL_SP_GRAVITY_ON_PORT_RIGHT,	MV_LEFT                              },
1529   { EL_SP_GRAVITY_ON_PORT_UP,		                             MV_DOWN },
1530   { EL_SP_GRAVITY_ON_PORT_DOWN,		                     MV_UP           },
1531   { EL_SP_GRAVITY_OFF_PORT_LEFT,	          MV_RIGHT                   },
1532   { EL_SP_GRAVITY_OFF_PORT_RIGHT,	MV_LEFT                              },
1533   { EL_SP_GRAVITY_OFF_PORT_UP,		                             MV_DOWN },
1534   { EL_SP_GRAVITY_OFF_PORT_DOWN,	                     MV_UP           },
1535 
1536   { EL_UNDEFINED,			MV_NONE				     }
1537 };
1538 
1539 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1540 
1541 #define IS_AUTO_CHANGING(e)	(element_info[e].has_change_event[CE_DELAY])
1542 #define IS_JUST_CHANGING(x, y)	(ChangeDelay[x][y] != 0)
1543 #define IS_CHANGING(x, y)	(IS_AUTO_CHANGING(Feld[x][y]) || \
1544 				 IS_JUST_CHANGING(x, y))
1545 
1546 #define CE_PAGE(e, ce)		(element_info[e].event_page[ce])
1547 
1548 /* static variables for playfield scan mode (scanning forward or backward) */
1549 static int playfield_scan_start_x = 0;
1550 static int playfield_scan_start_y = 0;
1551 static int playfield_scan_delta_x = 1;
1552 static int playfield_scan_delta_y = 1;
1553 
1554 #define SCAN_PLAYFIELD(x, y)	for ((y) = playfield_scan_start_y;	\
1555 				     (y) >= 0 && (y) <= lev_fieldy - 1;	\
1556 				     (y) += playfield_scan_delta_y)	\
1557 				for ((x) = playfield_scan_start_x;	\
1558 				     (x) >= 0 && (x) <= lev_fieldx - 1;	\
1559 				     (x) += playfield_scan_delta_x)
1560 
1561 #ifdef DEBUG
DEBUG_SetMaximumDynamite()1562 void DEBUG_SetMaximumDynamite()
1563 {
1564   int i;
1565 
1566   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1567     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1568       local_player->inventory_element[local_player->inventory_size++] =
1569 	EL_DYNAMITE;
1570 }
1571 #endif
1572 
InitPlayfieldScanModeVars()1573 static void InitPlayfieldScanModeVars()
1574 {
1575   if (game.use_reverse_scan_direction)
1576   {
1577     playfield_scan_start_x = lev_fieldx - 1;
1578     playfield_scan_start_y = lev_fieldy - 1;
1579 
1580     playfield_scan_delta_x = -1;
1581     playfield_scan_delta_y = -1;
1582   }
1583   else
1584   {
1585     playfield_scan_start_x = 0;
1586     playfield_scan_start_y = 0;
1587 
1588     playfield_scan_delta_x = 1;
1589     playfield_scan_delta_y = 1;
1590   }
1591 }
1592 
InitPlayfieldScanMode(int mode)1593 static void InitPlayfieldScanMode(int mode)
1594 {
1595   game.use_reverse_scan_direction =
1596     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1597 
1598   InitPlayfieldScanModeVars();
1599 }
1600 
get_move_delay_from_stepsize(int move_stepsize)1601 static int get_move_delay_from_stepsize(int move_stepsize)
1602 {
1603   move_stepsize =
1604     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1605 
1606   /* make sure that stepsize value is always a power of 2 */
1607   move_stepsize = (1 << log_2(move_stepsize));
1608 
1609   return TILEX / move_stepsize;
1610 }
1611 
SetPlayerMoveSpeed(struct PlayerInfo * player,int move_stepsize,boolean init_game)1612 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1613 			       boolean init_game)
1614 {
1615   int player_nr = player->index_nr;
1616   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1617   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1618 
1619   /* do no immediately change move delay -- the player might just be moving */
1620   player->move_delay_value_next = move_delay;
1621 
1622   /* information if player can move must be set separately */
1623   player->cannot_move = cannot_move;
1624 
1625   if (init_game)
1626   {
1627     player->move_delay       = game.initial_move_delay[player_nr];
1628     player->move_delay_value = game.initial_move_delay_value[player_nr];
1629 
1630     player->move_delay_value_next = -1;
1631 
1632     player->move_delay_reset_counter = 0;
1633   }
1634 }
1635 
GetPlayerConfig()1636 void GetPlayerConfig()
1637 {
1638   GameFrameDelay = setup.game_frame_delay;
1639 
1640   if (!audio.sound_available)
1641     setup.sound_simple = FALSE;
1642 
1643   if (!audio.loops_available)
1644     setup.sound_loops = FALSE;
1645 
1646   if (!audio.music_available)
1647     setup.sound_music = FALSE;
1648 
1649   if (!video.fullscreen_available)
1650     setup.fullscreen = FALSE;
1651 
1652   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1653 
1654   SetAudioMode(setup.sound);
1655   InitJoysticks();
1656 }
1657 
GetElementFromGroupElement(int element)1658 int GetElementFromGroupElement(int element)
1659 {
1660   if (IS_GROUP_ELEMENT(element))
1661   {
1662     struct ElementGroupInfo *group = element_info[element].group;
1663     int last_anim_random_frame = gfx.anim_random_frame;
1664     int element_pos;
1665 
1666     if (group->choice_mode == ANIM_RANDOM)
1667       gfx.anim_random_frame = RND(group->num_elements_resolved);
1668 
1669     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1670 				    group->choice_mode, 0,
1671 				    group->choice_pos);
1672 
1673     if (group->choice_mode == ANIM_RANDOM)
1674       gfx.anim_random_frame = last_anim_random_frame;
1675 
1676     group->choice_pos++;
1677 
1678     element = group->element_resolved[element_pos];
1679   }
1680 
1681   return element;
1682 }
1683 
InitPlayerField(int x,int y,int element,boolean init_game)1684 static void InitPlayerField(int x, int y, int element, boolean init_game)
1685 {
1686   if (element == EL_SP_MURPHY)
1687   {
1688     if (init_game)
1689     {
1690       if (stored_player[0].present)
1691       {
1692 	Feld[x][y] = EL_SP_MURPHY_CLONE;
1693 
1694 	return;
1695       }
1696       else
1697       {
1698 	stored_player[0].initial_element = element;
1699 	stored_player[0].use_murphy = TRUE;
1700 
1701 	if (!level.use_artwork_element[0])
1702 	  stored_player[0].artwork_element = EL_SP_MURPHY;
1703       }
1704 
1705       Feld[x][y] = EL_PLAYER_1;
1706     }
1707   }
1708 
1709   if (init_game)
1710   {
1711     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1712     int jx = player->jx, jy = player->jy;
1713 
1714     player->present = TRUE;
1715 
1716     player->block_last_field = (element == EL_SP_MURPHY ?
1717 				level.sp_block_last_field :
1718 				level.block_last_field);
1719 
1720     /* ---------- initialize player's last field block delay --------------- */
1721 
1722     /* always start with reliable default value (no adjustment needed) */
1723     player->block_delay_adjustment = 0;
1724 
1725     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1726     if (player->block_last_field && element == EL_SP_MURPHY)
1727       player->block_delay_adjustment = 1;
1728 
1729     /* special case 2: in game engines before 3.1.1, blocking was different */
1730     if (game.use_block_last_field_bug)
1731       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1732 
1733     if (!options.network || player->connected)
1734     {
1735       player->active = TRUE;
1736 
1737       /* remove potentially duplicate players */
1738       if (StorePlayer[jx][jy] == Feld[x][y])
1739 	StorePlayer[jx][jy] = 0;
1740 
1741       StorePlayer[x][y] = Feld[x][y];
1742 
1743       if (options.debug)
1744       {
1745 	printf("Player %d activated.\n", player->element_nr);
1746 	printf("[Local player is %d and currently %s.]\n",
1747 	       local_player->element_nr,
1748 	       local_player->active ? "active" : "not active");
1749       }
1750     }
1751 
1752     Feld[x][y] = EL_EMPTY;
1753 
1754     player->jx = player->last_jx = x;
1755     player->jy = player->last_jy = y;
1756   }
1757 
1758 #if USE_PLAYER_REANIMATION
1759   if (!init_game)
1760   {
1761     int player_nr = GET_PLAYER_NR(element);
1762     struct PlayerInfo *player = &stored_player[player_nr];
1763 
1764     if (player->active && player->killed)
1765       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1766   }
1767 #endif
1768 }
1769 
InitField(int x,int y,boolean init_game)1770 static void InitField(int x, int y, boolean init_game)
1771 {
1772   int element = Feld[x][y];
1773 
1774   switch (element)
1775   {
1776     case EL_SP_MURPHY:
1777     case EL_PLAYER_1:
1778     case EL_PLAYER_2:
1779     case EL_PLAYER_3:
1780     case EL_PLAYER_4:
1781       InitPlayerField(x, y, element, init_game);
1782       break;
1783 
1784     case EL_SOKOBAN_FIELD_PLAYER:
1785       element = Feld[x][y] = EL_PLAYER_1;
1786       InitField(x, y, init_game);
1787 
1788       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1789       InitField(x, y, init_game);
1790       break;
1791 
1792     case EL_SOKOBAN_FIELD_EMPTY:
1793       local_player->sokobanfields_still_needed++;
1794       break;
1795 
1796     case EL_STONEBLOCK:
1797       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1798 	Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1799       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1800 	Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1801       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1802 	Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1803       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1804 	Feld[x][y] = EL_ACID_POOL_BOTTOM;
1805       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1806 	Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1807       break;
1808 
1809     case EL_BUG:
1810     case EL_BUG_RIGHT:
1811     case EL_BUG_UP:
1812     case EL_BUG_LEFT:
1813     case EL_BUG_DOWN:
1814     case EL_SPACESHIP:
1815     case EL_SPACESHIP_RIGHT:
1816     case EL_SPACESHIP_UP:
1817     case EL_SPACESHIP_LEFT:
1818     case EL_SPACESHIP_DOWN:
1819     case EL_BD_BUTTERFLY:
1820     case EL_BD_BUTTERFLY_RIGHT:
1821     case EL_BD_BUTTERFLY_UP:
1822     case EL_BD_BUTTERFLY_LEFT:
1823     case EL_BD_BUTTERFLY_DOWN:
1824     case EL_BD_FIREFLY:
1825     case EL_BD_FIREFLY_RIGHT:
1826     case EL_BD_FIREFLY_UP:
1827     case EL_BD_FIREFLY_LEFT:
1828     case EL_BD_FIREFLY_DOWN:
1829     case EL_PACMAN_RIGHT:
1830     case EL_PACMAN_UP:
1831     case EL_PACMAN_LEFT:
1832     case EL_PACMAN_DOWN:
1833     case EL_YAMYAM:
1834     case EL_YAMYAM_LEFT:
1835     case EL_YAMYAM_RIGHT:
1836     case EL_YAMYAM_UP:
1837     case EL_YAMYAM_DOWN:
1838     case EL_DARK_YAMYAM:
1839     case EL_ROBOT:
1840     case EL_PACMAN:
1841     case EL_SP_SNIKSNAK:
1842     case EL_SP_ELECTRON:
1843     case EL_MOLE:
1844     case EL_MOLE_LEFT:
1845     case EL_MOLE_RIGHT:
1846     case EL_MOLE_UP:
1847     case EL_MOLE_DOWN:
1848       InitMovDir(x, y);
1849       break;
1850 
1851     case EL_AMOEBA_FULL:
1852     case EL_BD_AMOEBA:
1853       InitAmoebaNr(x, y);
1854       break;
1855 
1856     case EL_AMOEBA_DROP:
1857       if (y == lev_fieldy - 1)
1858       {
1859 	Feld[x][y] = EL_AMOEBA_GROWING;
1860 	Store[x][y] = EL_AMOEBA_WET;
1861       }
1862       break;
1863 
1864     case EL_DYNAMITE_ACTIVE:
1865     case EL_SP_DISK_RED_ACTIVE:
1866     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1867     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1868     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1869     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1870       MovDelay[x][y] = 96;
1871       break;
1872 
1873     case EL_EM_DYNAMITE_ACTIVE:
1874       MovDelay[x][y] = 32;
1875       break;
1876 
1877     case EL_LAMP:
1878       local_player->lights_still_needed++;
1879       break;
1880 
1881     case EL_PENGUIN:
1882       local_player->friends_still_needed++;
1883       break;
1884 
1885     case EL_PIG:
1886     case EL_DRAGON:
1887       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1888       break;
1889 
1890     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1891     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1892     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1893     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1894     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1895     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1896     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1897     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1898     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1899     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1900     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1901     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1902       if (init_game)
1903       {
1904 	int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1905 	int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1906 	int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1907 
1908 	if (game.belt_dir_nr[belt_nr] == 3)	/* initial value */
1909 	{
1910 	  game.belt_dir[belt_nr] = belt_dir;
1911 	  game.belt_dir_nr[belt_nr] = belt_dir_nr;
1912 	}
1913 	else	/* more than one switch -- set it like the first switch */
1914 	{
1915 	  Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1916 	}
1917       }
1918       break;
1919 
1920 #if !USE_BOTH_SWITCHGATE_SWITCHES
1921     case EL_SWITCHGATE_SWITCH_DOWN:	/* always start with same switch pos */
1922       if (init_game)
1923 	Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1924       break;
1925 
1926     case EL_DC_SWITCHGATE_SWITCH_DOWN:	/* always start with same switch pos */
1927       if (init_game)
1928 	Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1929       break;
1930 #endif
1931 
1932     case EL_LIGHT_SWITCH_ACTIVE:
1933       if (init_game)
1934 	game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1935       break;
1936 
1937     case EL_INVISIBLE_STEELWALL:
1938     case EL_INVISIBLE_WALL:
1939     case EL_INVISIBLE_SAND:
1940       if (game.light_time_left > 0 ||
1941 	  game.lenses_time_left > 0)
1942         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1943       break;
1944 
1945     case EL_EMC_MAGIC_BALL:
1946       if (game.ball_state)
1947 	Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1948       break;
1949 
1950     case EL_EMC_MAGIC_BALL_SWITCH:
1951       if (game.ball_state)
1952 	Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1953       break;
1954 
1955     case EL_TRIGGER_PLAYER:
1956     case EL_TRIGGER_ELEMENT:
1957     case EL_TRIGGER_CE_VALUE:
1958     case EL_TRIGGER_CE_SCORE:
1959     case EL_SELF:
1960     case EL_ANY_ELEMENT:
1961     case EL_CURRENT_CE_VALUE:
1962     case EL_CURRENT_CE_SCORE:
1963     case EL_PREV_CE_1:
1964     case EL_PREV_CE_2:
1965     case EL_PREV_CE_3:
1966     case EL_PREV_CE_4:
1967     case EL_PREV_CE_5:
1968     case EL_PREV_CE_6:
1969     case EL_PREV_CE_7:
1970     case EL_PREV_CE_8:
1971     case EL_NEXT_CE_1:
1972     case EL_NEXT_CE_2:
1973     case EL_NEXT_CE_3:
1974     case EL_NEXT_CE_4:
1975     case EL_NEXT_CE_5:
1976     case EL_NEXT_CE_6:
1977     case EL_NEXT_CE_7:
1978     case EL_NEXT_CE_8:
1979       /* reference elements should not be used on the playfield */
1980       Feld[x][y] = EL_EMPTY;
1981       break;
1982 
1983     default:
1984       if (IS_CUSTOM_ELEMENT(element))
1985       {
1986 	if (CAN_MOVE(element))
1987 	  InitMovDir(x, y);
1988 
1989 #if USE_NEW_CUSTOM_VALUE
1990 	if (!element_info[element].use_last_ce_value || init_game)
1991 	  CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1992 #endif
1993       }
1994       else if (IS_GROUP_ELEMENT(element))
1995       {
1996 	Feld[x][y] = GetElementFromGroupElement(element);
1997 
1998 	InitField(x, y, init_game);
1999       }
2000 
2001       break;
2002   }
2003 
2004   if (!init_game)
2005     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2006 }
2007 
InitField_WithBug1(int x,int y,boolean init_game)2008 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2009 {
2010   InitField(x, y, init_game);
2011 
2012   /* not needed to call InitMovDir() -- already done by InitField()! */
2013   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2014       CAN_MOVE(Feld[x][y]))
2015     InitMovDir(x, y);
2016 }
2017 
InitField_WithBug2(int x,int y,boolean init_game)2018 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2019 {
2020   int old_element = Feld[x][y];
2021 
2022   InitField(x, y, init_game);
2023 
2024   /* not needed to call InitMovDir() -- already done by InitField()! */
2025   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2026       CAN_MOVE(old_element) &&
2027       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2028     InitMovDir(x, y);
2029 
2030   /* this case is in fact a combination of not less than three bugs:
2031      first, it calls InitMovDir() for elements that can move, although this is
2032      already done by InitField(); then, it checks the element that was at this
2033      field _before_ the call to InitField() (which can change it); lastly, it
2034      was not called for "mole with direction" elements, which were treated as
2035      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2036   */
2037 }
2038 
get_key_element_from_nr(int key_nr)2039 static int get_key_element_from_nr(int key_nr)
2040 {
2041   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2042 			  level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2043 			  EL_EM_KEY_1 : EL_KEY_1);
2044 
2045   return key_base_element + key_nr;
2046 }
2047 
get_next_dropped_element(struct PlayerInfo * player)2048 static int get_next_dropped_element(struct PlayerInfo *player)
2049 {
2050   return (player->inventory_size > 0 ?
2051 	  player->inventory_element[player->inventory_size - 1] :
2052 	  player->inventory_infinite_element != EL_UNDEFINED ?
2053 	  player->inventory_infinite_element :
2054 	  player->dynabombs_left > 0 ?
2055 	  EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2056 	  EL_UNDEFINED);
2057 }
2058 
get_inventory_element_from_pos(struct PlayerInfo * player,int pos)2059 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2060 {
2061   /* pos >= 0: get element from bottom of the stack;
2062      pos <  0: get element from top of the stack */
2063 
2064   if (pos < 0)
2065   {
2066     int min_inventory_size = -pos;
2067     int inventory_pos = player->inventory_size - min_inventory_size;
2068     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2069 
2070     return (player->inventory_size >= min_inventory_size ?
2071 	    player->inventory_element[inventory_pos] :
2072 	    player->inventory_infinite_element != EL_UNDEFINED ?
2073 	    player->inventory_infinite_element :
2074 	    player->dynabombs_left >= min_dynabombs_left ?
2075 	    EL_DYNABOMB_PLAYER_1 + player->index_nr :
2076 	    EL_UNDEFINED);
2077   }
2078   else
2079   {
2080     int min_dynabombs_left = pos + 1;
2081     int min_inventory_size = pos + 1 - player->dynabombs_left;
2082     int inventory_pos = pos - player->dynabombs_left;
2083 
2084     return (player->inventory_infinite_element != EL_UNDEFINED ?
2085 	    player->inventory_infinite_element :
2086 	    player->dynabombs_left >= min_dynabombs_left ?
2087 	    EL_DYNABOMB_PLAYER_1 + player->index_nr :
2088 	    player->inventory_size >= min_inventory_size ?
2089 	    player->inventory_element[inventory_pos] :
2090 	    EL_UNDEFINED);
2091   }
2092 }
2093 
compareGamePanelOrderInfo(const void * object1,const void * object2)2094 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2095 {
2096   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2097   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2098   int compare_result;
2099 
2100   if (gpo1->sort_priority != gpo2->sort_priority)
2101     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2102   else
2103     compare_result = gpo1->nr - gpo2->nr;
2104 
2105   return compare_result;
2106 }
2107 
InitGameControlValues()2108 void InitGameControlValues()
2109 {
2110   int i;
2111 
2112   for (i = 0; game_panel_controls[i].nr != -1; i++)
2113   {
2114     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2115     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2116     struct TextPosInfo *pos = gpc->pos;
2117     int nr = gpc->nr;
2118     int type = gpc->type;
2119 
2120     if (nr != i)
2121     {
2122       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2123       Error(ERR_EXIT, "this should not happen -- please debug");
2124     }
2125 
2126     /* force update of game controls after initialization */
2127     gpc->value = gpc->last_value = -1;
2128     gpc->frame = gpc->last_frame = -1;
2129     gpc->gfx_frame = -1;
2130 
2131     /* determine panel value width for later calculation of alignment */
2132     if (type == TYPE_INTEGER || type == TYPE_STRING)
2133     {
2134       pos->width = pos->size * getFontWidth(pos->font);
2135       pos->height = getFontHeight(pos->font);
2136     }
2137     else if (type == TYPE_ELEMENT)
2138     {
2139       pos->width = pos->size;
2140       pos->height = pos->size;
2141     }
2142 
2143     /* fill structure for game panel draw order */
2144     gpo->nr = gpc->nr;
2145     gpo->sort_priority = pos->sort_priority;
2146   }
2147 
2148   /* sort game panel controls according to sort_priority and control number */
2149   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2150 	sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2151 }
2152 
UpdatePlayfieldElementCount()2153 void UpdatePlayfieldElementCount()
2154 {
2155   boolean use_element_count = FALSE;
2156   int i, j, x, y;
2157 
2158   /* first check if it is needed at all to calculate playfield element count */
2159   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2160     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2161       use_element_count = TRUE;
2162 
2163   if (!use_element_count)
2164     return;
2165 
2166   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2167     element_info[i].element_count = 0;
2168 
2169   SCAN_PLAYFIELD(x, y)
2170   {
2171     element_info[Feld[x][y]].element_count++;
2172   }
2173 
2174   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2175     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2176       if (IS_IN_GROUP(j, i))
2177 	element_info[EL_GROUP_START + i].element_count +=
2178 	  element_info[j].element_count;
2179 }
2180 
UpdateGameControlValues()2181 void UpdateGameControlValues()
2182 {
2183   int i, k;
2184   int time = (local_player->LevelSolved ?
2185 	      local_player->LevelSolved_CountingTime :
2186 	      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2187 	      level.native_em_level->lev->time :
2188 	      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2189 	      level.native_sp_level->game_sp->time_played :
2190 	      game.no_time_limit ? TimePlayed : TimeLeft);
2191   int score = (local_player->LevelSolved ?
2192 	       local_player->LevelSolved_CountingScore :
2193 	       level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2194 	       level.native_em_level->lev->score :
2195 	       level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2196 	       level.native_sp_level->game_sp->score :
2197 	       local_player->score);
2198   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2199 	      level.native_em_level->lev->required :
2200 	      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2201 	      level.native_sp_level->game_sp->infotrons_still_needed :
2202 	      local_player->gems_still_needed);
2203   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2204 		     level.native_em_level->lev->required > 0 :
2205 		     level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2206 		     level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2207 		     local_player->gems_still_needed > 0 ||
2208 		     local_player->sokobanfields_still_needed > 0 ||
2209 		     local_player->lights_still_needed > 0);
2210 
2211   UpdatePlayfieldElementCount();
2212 
2213   /* update game panel control values */
2214 
2215   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2216   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2217 
2218   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2219   for (i = 0; i < MAX_NUM_KEYS; i++)
2220     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2221   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2222   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2223 
2224   if (game.centered_player_nr == -1)
2225   {
2226     for (i = 0; i < MAX_PLAYERS; i++)
2227     {
2228       /* only one player in Supaplex game engine */
2229       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2230 	break;
2231 
2232       for (k = 0; k < MAX_NUM_KEYS; k++)
2233       {
2234 	if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2235 	{
2236 	  if (level.native_em_level->ply[i]->keys & (1 << k))
2237 	    game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2238 	      get_key_element_from_nr(k);
2239 	}
2240 	else if (stored_player[i].key[k])
2241 	  game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2242 	    get_key_element_from_nr(k);
2243       }
2244 
2245       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2246 	game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2247 	  level.native_em_level->ply[i]->dynamite;
2248       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2249 	game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2250 	  level.native_sp_level->game_sp->red_disk_count;
2251       else
2252 	game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2253 	  stored_player[i].inventory_size;
2254 
2255       if (stored_player[i].num_white_keys > 0)
2256 	game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2257 	  EL_DC_KEY_WHITE;
2258 
2259       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2260 	stored_player[i].num_white_keys;
2261     }
2262   }
2263   else
2264   {
2265     int player_nr = game.centered_player_nr;
2266 
2267     for (k = 0; k < MAX_NUM_KEYS; k++)
2268     {
2269       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270       {
2271 	if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2272 	  game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2273 	    get_key_element_from_nr(k);
2274       }
2275       else if (stored_player[player_nr].key[k])
2276 	game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2277 	  get_key_element_from_nr(k);
2278     }
2279 
2280     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2281       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2282 	level.native_em_level->ply[player_nr]->dynamite;
2283     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2284       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2285 	level.native_sp_level->game_sp->red_disk_count;
2286     else
2287       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2288 	stored_player[player_nr].inventory_size;
2289 
2290     if (stored_player[player_nr].num_white_keys > 0)
2291       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2292 
2293     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2294       stored_player[player_nr].num_white_keys;
2295   }
2296 
2297   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2298   {
2299     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2300       get_inventory_element_from_pos(local_player, i);
2301     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2302       get_inventory_element_from_pos(local_player, -i - 1);
2303   }
2304 
2305   game_panel_controls[GAME_PANEL_SCORE].value = score;
2306   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2307 
2308   game_panel_controls[GAME_PANEL_TIME].value = time;
2309 
2310   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2311   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2312   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2313 
2314   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2315 
2316   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2317     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2318      EL_EMPTY);
2319   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2320     local_player->shield_normal_time_left;
2321   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2322     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2323      EL_EMPTY);
2324   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2325     local_player->shield_deadly_time_left;
2326 
2327   game_panel_controls[GAME_PANEL_EXIT].value =
2328     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2329 
2330   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2331     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2332   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2333     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2334      EL_EMC_MAGIC_BALL_SWITCH);
2335 
2336   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2337     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2338   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2339     game.light_time_left;
2340 
2341   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2342     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2343   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2344     game.timegate_time_left;
2345 
2346   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2347     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2348 
2349   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2350     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2351   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2352     game.lenses_time_left;
2353 
2354   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2355     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2356   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2357     game.magnify_time_left;
2358 
2359   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2360     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2361      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2362      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2363      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2364      EL_BALLOON_SWITCH_NONE);
2365 
2366   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2367     local_player->dynabomb_count;
2368   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2369     local_player->dynabomb_size;
2370   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2371     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2372 
2373   game_panel_controls[GAME_PANEL_PENGUINS].value =
2374     local_player->friends_still_needed;
2375 
2376   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2377     local_player->sokobanfields_still_needed;
2378   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2379     local_player->sokobanfields_still_needed;
2380 
2381   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2382     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2383 
2384   for (i = 0; i < NUM_BELTS; i++)
2385   {
2386     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2387       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2388        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2389     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2390       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2391   }
2392 
2393   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2394     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2395   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2396     game.magic_wall_time_left;
2397 
2398 #if USE_PLAYER_GRAVITY
2399   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2400     local_player->gravity;
2401 #else
2402   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2403 #endif
2404 
2405   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2406     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2407 
2408   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2409     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2410       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2411        game.panel.element[i].id : EL_UNDEFINED);
2412 
2413   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2414     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2415       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2416        element_info[game.panel.element_count[i].id].element_count : 0);
2417 
2418   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2419     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2420       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2421        element_info[game.panel.ce_score[i].id].collect_score : 0);
2422 
2423   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2424     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2425       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2426        element_info[game.panel.ce_score_element[i].id].collect_score :
2427        EL_UNDEFINED);
2428 
2429   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2430   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2431   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2432 
2433   /* update game panel control frames */
2434 
2435   for (i = 0; game_panel_controls[i].nr != -1; i++)
2436   {
2437     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2438 
2439     if (gpc->type == TYPE_ELEMENT)
2440     {
2441       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2442       {
2443 	int last_anim_random_frame = gfx.anim_random_frame;
2444 	int element = gpc->value;
2445 	int graphic = el2panelimg(element);
2446 
2447 	if (gpc->value != gpc->last_value)
2448 	{
2449 	  gpc->gfx_frame = 0;
2450 	  gpc->gfx_random = INIT_GFX_RANDOM();
2451 	}
2452 	else
2453 	{
2454 	  gpc->gfx_frame++;
2455 
2456 	  if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2457 	      IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2458 	    gpc->gfx_random = INIT_GFX_RANDOM();
2459 	}
2460 
2461 	if (ANIM_MODE(graphic) == ANIM_RANDOM)
2462 	  gfx.anim_random_frame = gpc->gfx_random;
2463 
2464 	if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2465 	  gpc->gfx_frame = element_info[element].collect_score;
2466 
2467 	gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2468 					      gpc->gfx_frame);
2469 
2470 	if (ANIM_MODE(graphic) == ANIM_RANDOM)
2471 	  gfx.anim_random_frame = last_anim_random_frame;
2472       }
2473     }
2474   }
2475 }
2476 
DisplayGameControlValues()2477 void DisplayGameControlValues()
2478 {
2479   boolean redraw_panel = FALSE;
2480   int i;
2481 
2482   for (i = 0; game_panel_controls[i].nr != -1; i++)
2483   {
2484     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2485 
2486     if (PANEL_DEACTIVATED(gpc->pos))
2487       continue;
2488 
2489     if (gpc->value == gpc->last_value &&
2490 	gpc->frame == gpc->last_frame)
2491       continue;
2492 
2493     redraw_panel = TRUE;
2494   }
2495 
2496   if (!redraw_panel)
2497     return;
2498 
2499   /* copy default game door content to main double buffer */
2500 #if 1
2501   /* !!! CHECK AGAIN !!! */
2502   SetPanelBackground();
2503   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2504   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2505 #else
2506   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2507 	     DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2508 #endif
2509 
2510   /* redraw game control buttons */
2511 #if 1
2512   RedrawGameButtons();
2513 #else
2514   UnmapGameButtons();
2515   MapGameButtons();
2516 #endif
2517 
2518   game_status = GAME_MODE_PSEUDO_PANEL;
2519 
2520 #if 1
2521   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2522 #else
2523   for (i = 0; game_panel_controls[i].nr != -1; i++)
2524 #endif
2525   {
2526 #if 1
2527     int nr = game_panel_order[i].nr;
2528     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2529 #else
2530     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2531     int nr = gpc->nr;
2532 #endif
2533     struct TextPosInfo *pos = gpc->pos;
2534     int type = gpc->type;
2535     int value = gpc->value;
2536     int frame = gpc->frame;
2537 #if 0
2538     int last_value = gpc->last_value;
2539     int last_frame = gpc->last_frame;
2540 #endif
2541     int size = pos->size;
2542     int font = pos->font;
2543     boolean draw_masked = pos->draw_masked;
2544     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2545 
2546     if (PANEL_DEACTIVATED(pos))
2547       continue;
2548 
2549 #if 0
2550     if (value == last_value && frame == last_frame)
2551       continue;
2552 #endif
2553 
2554     gpc->last_value = value;
2555     gpc->last_frame = frame;
2556 
2557 #if 0
2558     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2559 #endif
2560 
2561     if (type == TYPE_INTEGER)
2562     {
2563       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2564 	  nr == GAME_PANEL_TIME)
2565       {
2566 	boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2567 
2568 	if (use_dynamic_size)		/* use dynamic number of digits */
2569 	{
2570 	  int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2571 	  int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2572 	  int size2 = size1 + 1;
2573 	  int font1 = pos->font;
2574 	  int font2 = pos->font_alt;
2575 
2576 	  size = (value < value_change ? size1 : size2);
2577 	  font = (value < value_change ? font1 : font2);
2578 
2579 #if 0
2580 	  /* clear background if value just changed its size (dynamic digits) */
2581 	  if ((last_value < value_change) != (value < value_change))
2582 	  {
2583 	    int width1 = size1 * getFontWidth(font1);
2584 	    int width2 = size2 * getFontWidth(font2);
2585 	    int max_width = MAX(width1, width2);
2586 	    int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2587 
2588 	    pos->width = max_width;
2589 
2590 	    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2591 				       max_width, max_height);
2592 	  }
2593 #endif
2594 	}
2595       }
2596 
2597 #if 1
2598       /* correct text size if "digits" is zero or less */
2599       if (size <= 0)
2600 	size = strlen(int2str(value, size));
2601 
2602       /* dynamically correct text alignment */
2603       pos->width = size * getFontWidth(font);
2604 #endif
2605 
2606       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2607 		  int2str(value, size), font, mask_mode);
2608     }
2609     else if (type == TYPE_ELEMENT)
2610     {
2611       int element, graphic;
2612       Bitmap *src_bitmap;
2613       int src_x, src_y;
2614       int width, height;
2615       int dst_x = PANEL_XPOS(pos);
2616       int dst_y = PANEL_YPOS(pos);
2617 
2618 #if 1
2619       if (value != EL_UNDEFINED && value != EL_EMPTY)
2620       {
2621 	element = value;
2622 	graphic = el2panelimg(value);
2623 
2624 	// printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2625 
2626 #if 1
2627 	if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2628 	  size = TILESIZE;
2629 #endif
2630 
2631 	getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2632 			      &src_x, &src_y);
2633 
2634 	width  = graphic_info[graphic].width  * size / TILESIZE;
2635 	height = graphic_info[graphic].height * size / TILESIZE;
2636 
2637 	if (draw_masked)
2638 	{
2639 	  SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2640 			dst_x - src_x, dst_y - src_y);
2641 	  BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2642 			   dst_x, dst_y);
2643 	}
2644 	else
2645 	{
2646 	  BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2647 		     dst_x, dst_y);
2648 	}
2649       }
2650 #else
2651       if (value == EL_UNDEFINED || value == EL_EMPTY)
2652       {
2653 	element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2654 	graphic = el2panelimg(element);
2655 
2656 	src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2657 	src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2658 	src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2659       }
2660       else
2661       {
2662 	element = value;
2663 	graphic = el2panelimg(value);
2664 
2665 	getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2666       }
2667 
2668       width  = graphic_info[graphic].width  * size / TILESIZE;
2669       height = graphic_info[graphic].height * size / TILESIZE;
2670 
2671       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2672 #endif
2673     }
2674     else if (type == TYPE_STRING)
2675     {
2676       boolean active = (value != 0);
2677       char *state_normal = "off";
2678       char *state_active = "on";
2679       char *state = (active ? state_active : state_normal);
2680       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2681 		 nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2682 		 nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2683 		 nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2684 
2685       if (nr == GAME_PANEL_GRAVITY_STATE)
2686       {
2687 	int font1 = pos->font;		/* (used for normal state) */
2688 	int font2 = pos->font_alt;	/* (used for active state) */
2689 #if 0
2690 	int size1 = strlen(state_normal);
2691 	int size2 = strlen(state_active);
2692 	int width1 = size1 * getFontWidth(font1);
2693 	int width2 = size2 * getFontWidth(font2);
2694 	int max_width = MAX(width1, width2);
2695 	int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2696 
2697 	pos->width = max_width;
2698 
2699 	/* clear background for values that may have changed its size */
2700 	ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2701 				   max_width, max_height);
2702 #endif
2703 
2704 	font = (active ? font2 : font1);
2705       }
2706 
2707       if (s != NULL)
2708       {
2709 	char *s_cut;
2710 
2711 #if 1
2712 	if (size <= 0)
2713 	{
2714 	  /* don't truncate output if "chars" is zero or less */
2715 	  size = strlen(s);
2716 
2717 	  /* dynamically correct text alignment */
2718 	  pos->width = size * getFontWidth(font);
2719 	}
2720 #endif
2721 
2722 	s_cut = getStringCopyN(s, size);
2723 
2724 	DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2725 		    s_cut, font, mask_mode);
2726 
2727 	free(s_cut);
2728       }
2729     }
2730 
2731     redraw_mask |= REDRAW_DOOR_1;
2732   }
2733 
2734   game_status = GAME_MODE_PLAYING;
2735 }
2736 
UpdateAndDisplayGameControlValues()2737 void UpdateAndDisplayGameControlValues()
2738 {
2739   if (tape.warp_forward)
2740     return;
2741 
2742   UpdateGameControlValues();
2743   DisplayGameControlValues();
2744 }
2745 
DrawGameValue_Emeralds(int value)2746 void DrawGameValue_Emeralds(int value)
2747 {
2748   struct TextPosInfo *pos = &game.panel.gems;
2749   int font_nr = pos->font;
2750   int font_width = getFontWidth(font_nr);
2751   int chars = pos->size;
2752 
2753 #if 1
2754   return;	/* !!! USE NEW STUFF !!! */
2755 #endif
2756 
2757   if (PANEL_DEACTIVATED(pos))
2758     return;
2759 
2760   pos->width = chars * font_width;
2761 
2762   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2763 }
2764 
DrawGameValue_Dynamite(int value)2765 void DrawGameValue_Dynamite(int value)
2766 {
2767   struct TextPosInfo *pos = &game.panel.inventory_count;
2768   int font_nr = pos->font;
2769   int font_width = getFontWidth(font_nr);
2770   int chars = pos->size;
2771 
2772 #if 1
2773   return;	/* !!! USE NEW STUFF !!! */
2774 #endif
2775 
2776   if (PANEL_DEACTIVATED(pos))
2777     return;
2778 
2779   pos->width = chars * font_width;
2780 
2781   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2782 }
2783 
DrawGameValue_Score(int value)2784 void DrawGameValue_Score(int value)
2785 {
2786   struct TextPosInfo *pos = &game.panel.score;
2787   int font_nr = pos->font;
2788   int font_width = getFontWidth(font_nr);
2789   int chars = pos->size;
2790 
2791 #if 1
2792   return;	/* !!! USE NEW STUFF !!! */
2793 #endif
2794 
2795   if (PANEL_DEACTIVATED(pos))
2796     return;
2797 
2798   pos->width = chars * font_width;
2799 
2800   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 }
2802 
DrawGameValue_Time(int value)2803 void DrawGameValue_Time(int value)
2804 {
2805   struct TextPosInfo *pos = &game.panel.time;
2806   static int last_value = -1;
2807   int chars1 = 3;
2808   int chars2 = 4;
2809   int chars = pos->size;
2810   int font1_nr = pos->font;
2811   int font2_nr = pos->font_alt;
2812   int font_nr = font1_nr;
2813   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2814 
2815 #if 1
2816   return;	/* !!! USE NEW STUFF !!! */
2817 #endif
2818 
2819   if (PANEL_DEACTIVATED(pos))
2820     return;
2821 
2822   if (use_dynamic_chars)		/* use dynamic number of chars */
2823   {
2824     chars   = (value < 1000 ? chars1   : chars2);
2825     font_nr = (value < 1000 ? font1_nr : font2_nr);
2826   }
2827 
2828   /* clear background if value just changed its size (dynamic chars only) */
2829   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2830   {
2831     int width1 = chars1 * getFontWidth(font1_nr);
2832     int width2 = chars2 * getFontWidth(font2_nr);
2833     int max_width = MAX(width1, width2);
2834     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2835 
2836     pos->width = max_width;
2837 
2838     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2839 			       max_width, max_height);
2840   }
2841 
2842   pos->width = chars * getFontWidth(font_nr);
2843 
2844   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2845 
2846   last_value = value;
2847 }
2848 
DrawGameValue_Level(int value)2849 void DrawGameValue_Level(int value)
2850 {
2851   struct TextPosInfo *pos = &game.panel.level_number;
2852   int chars1 = 2;
2853   int chars2 = 3;
2854   int chars = pos->size;
2855   int font1_nr = pos->font;
2856   int font2_nr = pos->font_alt;
2857   int font_nr = font1_nr;
2858   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2859 
2860 #if 1
2861   return;	/* !!! USE NEW STUFF !!! */
2862 #endif
2863 
2864   if (PANEL_DEACTIVATED(pos))
2865     return;
2866 
2867   if (use_dynamic_chars)		/* use dynamic number of chars */
2868   {
2869     chars   = (level_nr < 100 ? chars1   : chars2);
2870     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2871   }
2872 
2873   pos->width = chars * getFontWidth(font_nr);
2874 
2875   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2876 }
2877 
DrawGameValue_Keys(int key[MAX_NUM_KEYS])2878 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2879 {
2880   int i;
2881 
2882 #if 1
2883   return;	/* !!! USE NEW STUFF !!! */
2884 #endif
2885 
2886   for (i = 0; i < MAX_NUM_KEYS; i++)
2887   {
2888     struct TextPosInfo *pos = &game.panel.key[i];
2889     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2890     int src_y = DOOR_GFX_PAGEY1 + 123;
2891     int dst_x = PANEL_XPOS(pos);
2892     int dst_y = PANEL_YPOS(pos);
2893 
2894     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2895 		   level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2896 		   EL_KEY_1) + i;
2897     int graphic = el2edimg(element);
2898 
2899     if (PANEL_DEACTIVATED(pos))
2900       continue;
2901 
2902 #if 0
2903     /* masked blit with tiles from half-size scaled bitmap does not work yet
2904        (no mask bitmap created for these sizes after loading and scaling) --
2905        solution: load without creating mask, scale, then create final mask */
2906 
2907     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2908 	       MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2909 
2910     if (key[i])
2911     {
2912       Bitmap *src_bitmap;
2913       int src_x, src_y;
2914 
2915       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2916 
2917       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2918 		    dst_x - src_x, dst_y - src_y);
2919       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2920 		       dst_x, dst_y);
2921     }
2922 #else
2923     if (key[i])
2924       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2925     else
2926       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2927 		 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2928 #endif
2929   }
2930 }
2931 
DrawAllGameValues(int emeralds,int dynamite,int score,int time,int key_bits)2932 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
2933 		       int key_bits)
2934 {
2935   int key[MAX_NUM_KEYS];
2936   int i;
2937 
2938   /* prevent EM engine from updating time/score values parallel to GameWon() */
2939   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
2940       local_player->LevelSolved)
2941     return;
2942 
2943   for (i = 0; i < MAX_NUM_KEYS; i++)
2944     key[i] = key_bits & (1 << i);
2945 
2946   DrawGameValue_Level(level_nr);
2947 
2948   DrawGameValue_Emeralds(emeralds);
2949   DrawGameValue_Dynamite(dynamite);
2950   DrawGameValue_Score(score);
2951   DrawGameValue_Time(time);
2952 
2953   DrawGameValue_Keys(key);
2954 }
2955 
UpdateGameDoorValues()2956 void UpdateGameDoorValues()
2957 {
2958   UpdateGameControlValues();
2959 }
2960 
DrawGameDoorValues()2961 void DrawGameDoorValues()
2962 {
2963   DisplayGameControlValues();
2964 }
2965 
DrawGameDoorValues_OLD()2966 void DrawGameDoorValues_OLD()
2967 {
2968   int time_value = (game.no_time_limit ? TimePlayed : TimeLeft);
2969   int dynamite_value = 0;
2970   int score_value = (local_player->LevelSolved ? local_player->score_final :
2971 		     local_player->score);
2972   int gems_value = local_player->gems_still_needed;
2973   int key_bits = 0;
2974   int i, j;
2975 
2976   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2977   {
2978     DrawGameDoorValues_EM();
2979 
2980     return;
2981   }
2982 
2983   if (game.centered_player_nr == -1)
2984   {
2985     for (i = 0; i < MAX_PLAYERS; i++)
2986     {
2987       for (j = 0; j < MAX_NUM_KEYS; j++)
2988 	if (stored_player[i].key[j])
2989 	  key_bits |= (1 << j);
2990 
2991       dynamite_value += stored_player[i].inventory_size;
2992     }
2993   }
2994   else
2995   {
2996     int player_nr = game.centered_player_nr;
2997 
2998     for (i = 0; i < MAX_NUM_KEYS; i++)
2999       if (stored_player[player_nr].key[i])
3000 	key_bits |= (1 << i);
3001 
3002     dynamite_value = stored_player[player_nr].inventory_size;
3003   }
3004 
3005   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3006 		    key_bits);
3007 }
3008 
3009 
3010 /*
3011   =============================================================================
3012   InitGameEngine()
3013   -----------------------------------------------------------------------------
3014   initialize game engine due to level / tape version number
3015   =============================================================================
3016 */
3017 
InitGameEngine()3018 static void InitGameEngine()
3019 {
3020   int i, j, k, l, x, y;
3021 
3022   /* set game engine from tape file when re-playing, else from level file */
3023   game.engine_version = (tape.playing ? tape.engine_version :
3024 			 level.game_version);
3025 
3026   /* ---------------------------------------------------------------------- */
3027   /* set flags for bugs and changes according to active game engine version */
3028   /* ---------------------------------------------------------------------- */
3029 
3030   /*
3031     Summary of bugfix/change:
3032     Fixed handling for custom elements that change when pushed by the player.
3033 
3034     Fixed/changed in version:
3035     3.1.0
3036 
3037     Description:
3038     Before 3.1.0, custom elements that "change when pushing" changed directly
3039     after the player started pushing them (until then handled in "DigField()").
3040     Since 3.1.0, these custom elements are not changed until the "pushing"
3041     move of the element is finished (now handled in "ContinueMoving()").
3042 
3043     Affected levels/tapes:
3044     The first condition is generally needed for all levels/tapes before version
3045     3.1.0, which might use the old behaviour before it was changed; known tapes
3046     that are affected are some tapes from the level set "Walpurgis Gardens" by
3047     Jamie Cullen.
3048     The second condition is an exception from the above case and is needed for
3049     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3050     above (including some development versions of 3.1.0), but before it was
3051     known that this change would break tapes like the above and was fixed in
3052     3.1.1, so that the changed behaviour was active although the engine version
3053     while recording maybe was before 3.1.0. There is at least one tape that is
3054     affected by this exception, which is the tape for the one-level set "Bug
3055     Machine" by Juergen Bonhagen.
3056   */
3057 
3058   game.use_change_when_pushing_bug =
3059     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3060      !(tape.playing &&
3061        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3062        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3063 
3064   /*
3065     Summary of bugfix/change:
3066     Fixed handling for blocking the field the player leaves when moving.
3067 
3068     Fixed/changed in version:
3069     3.1.1
3070 
3071     Description:
3072     Before 3.1.1, when "block last field when moving" was enabled, the field
3073     the player is leaving when moving was blocked for the time of the move,
3074     and was directly unblocked afterwards. This resulted in the last field
3075     being blocked for exactly one less than the number of frames of one player
3076     move. Additionally, even when blocking was disabled, the last field was
3077     blocked for exactly one frame.
3078     Since 3.1.1, due to changes in player movement handling, the last field
3079     is not blocked at all when blocking is disabled. When blocking is enabled,
3080     the last field is blocked for exactly the number of frames of one player
3081     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3082     last field is blocked for exactly one more than the number of frames of
3083     one player move.
3084 
3085     Affected levels/tapes:
3086     (!!! yet to be determined -- probably many !!!)
3087   */
3088 
3089   game.use_block_last_field_bug =
3090     (game.engine_version < VERSION_IDENT(3,1,1,0));
3091 
3092   /*
3093     Summary of bugfix/change:
3094     Changed behaviour of CE changes with multiple changes per single frame.
3095 
3096     Fixed/changed in version:
3097     3.2.0-6
3098 
3099     Description:
3100     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3101     This resulted in race conditions where CEs seem to behave strange in some
3102     situations (where triggered CE changes were just skipped because there was
3103     already a CE change on that tile in the playfield in that engine frame).
3104     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3105     (The number of changes per frame must be limited in any case, because else
3106     it is easily possible to define CE changes that would result in an infinite
3107     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3108     should be set large enough so that it would only be reached in cases where
3109     the corresponding CE change conditions run into a loop. Therefore, it seems
3110     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3111     maximal number of change pages for custom elements.)
3112 
3113     Affected levels/tapes:
3114     Probably many.
3115   */
3116 
3117 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3118   game.max_num_changes_per_frame = 1;
3119 #else
3120   game.max_num_changes_per_frame =
3121     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3122 #endif
3123 
3124   /* ---------------------------------------------------------------------- */
3125 
3126   /* default scan direction: scan playfield from top/left to bottom/right */
3127   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3128 
3129   /* dynamically adjust element properties according to game engine version */
3130   InitElementPropertiesEngine(game.engine_version);
3131 
3132 #if 0
3133   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3134   printf("          tape version == %06d [%s] [file: %06d]\n",
3135 	 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3136 	 tape.file_version);
3137   printf("       => game.engine_version == %06d\n", game.engine_version);
3138 #endif
3139 
3140   /* ---------- initialize player's initial move delay --------------------- */
3141 
3142   /* dynamically adjust player properties according to level information */
3143   for (i = 0; i < MAX_PLAYERS; i++)
3144     game.initial_move_delay_value[i] =
3145       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3146 
3147   /* dynamically adjust player properties according to game engine version */
3148   for (i = 0; i < MAX_PLAYERS; i++)
3149     game.initial_move_delay[i] =
3150       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3151        game.initial_move_delay_value[i] : 0);
3152 
3153   /* ---------- initialize player's initial push delay --------------------- */
3154 
3155   /* dynamically adjust player properties according to game engine version */
3156   game.initial_push_delay_value =
3157     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3158 
3159   /* ---------- initialize changing elements ------------------------------- */
3160 
3161   /* initialize changing elements information */
3162   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3163   {
3164     struct ElementInfo *ei = &element_info[i];
3165 
3166     /* this pointer might have been changed in the level editor */
3167     ei->change = &ei->change_page[0];
3168 
3169     if (!IS_CUSTOM_ELEMENT(i))
3170     {
3171       ei->change->target_element = EL_EMPTY_SPACE;
3172       ei->change->delay_fixed = 0;
3173       ei->change->delay_random = 0;
3174       ei->change->delay_frames = 1;
3175     }
3176 
3177     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3178     {
3179       ei->has_change_event[j] = FALSE;
3180 
3181       ei->event_page_nr[j] = 0;
3182       ei->event_page[j] = &ei->change_page[0];
3183     }
3184   }
3185 
3186   /* add changing elements from pre-defined list */
3187   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3188   {
3189     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3190     struct ElementInfo *ei = &element_info[ch_delay->element];
3191 
3192     ei->change->target_element       = ch_delay->target_element;
3193     ei->change->delay_fixed          = ch_delay->change_delay;
3194 
3195     ei->change->pre_change_function  = ch_delay->pre_change_function;
3196     ei->change->change_function      = ch_delay->change_function;
3197     ei->change->post_change_function = ch_delay->post_change_function;
3198 
3199     ei->change->can_change = TRUE;
3200     ei->change->can_change_or_has_action = TRUE;
3201 
3202     ei->has_change_event[CE_DELAY] = TRUE;
3203 
3204     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3205     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3206   }
3207 
3208   /* ---------- initialize internal run-time variables --------------------- */
3209 
3210   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3211   {
3212     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3213 
3214     for (j = 0; j < ei->num_change_pages; j++)
3215     {
3216       ei->change_page[j].can_change_or_has_action =
3217 	(ei->change_page[j].can_change |
3218 	 ei->change_page[j].has_action);
3219     }
3220   }
3221 
3222   /* add change events from custom element configuration */
3223   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3224   {
3225     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3226 
3227     for (j = 0; j < ei->num_change_pages; j++)
3228     {
3229       if (!ei->change_page[j].can_change_or_has_action)
3230 	continue;
3231 
3232       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3233       {
3234 	/* only add event page for the first page found with this event */
3235 	if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3236 	{
3237 	  ei->has_change_event[k] = TRUE;
3238 
3239 	  ei->event_page_nr[k] = j;
3240 	  ei->event_page[k] = &ei->change_page[j];
3241 	}
3242       }
3243     }
3244   }
3245 
3246 #if 1
3247   /* ---------- initialize reference elements in change conditions --------- */
3248 
3249   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3250   {
3251     int element = EL_CUSTOM_START + i;
3252     struct ElementInfo *ei = &element_info[element];
3253 
3254     for (j = 0; j < ei->num_change_pages; j++)
3255     {
3256       int trigger_element = ei->change_page[j].initial_trigger_element;
3257 
3258       if (trigger_element >= EL_PREV_CE_8 &&
3259 	  trigger_element <= EL_NEXT_CE_8)
3260 	trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3261 
3262       ei->change_page[j].trigger_element = trigger_element;
3263     }
3264   }
3265 #endif
3266 
3267   /* ---------- initialize run-time trigger player and element ------------- */
3268 
3269   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3270   {
3271     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3272 
3273     for (j = 0; j < ei->num_change_pages; j++)
3274     {
3275       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3276       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3277       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3278       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3279       ei->change_page[j].actual_trigger_ce_value = 0;
3280       ei->change_page[j].actual_trigger_ce_score = 0;
3281     }
3282   }
3283 
3284   /* ---------- initialize trigger events ---------------------------------- */
3285 
3286   /* initialize trigger events information */
3287   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3288     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3289       trigger_events[i][j] = FALSE;
3290 
3291   /* add trigger events from element change event properties */
3292   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3293   {
3294     struct ElementInfo *ei = &element_info[i];
3295 
3296     for (j = 0; j < ei->num_change_pages; j++)
3297     {
3298       if (!ei->change_page[j].can_change_or_has_action)
3299 	continue;
3300 
3301       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3302       {
3303 	int trigger_element = ei->change_page[j].trigger_element;
3304 
3305 	for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3306 	{
3307 	  if (ei->change_page[j].has_event[k])
3308 	  {
3309 	    if (IS_GROUP_ELEMENT(trigger_element))
3310 	    {
3311 	      struct ElementGroupInfo *group =
3312 		element_info[trigger_element].group;
3313 
3314 	      for (l = 0; l < group->num_elements_resolved; l++)
3315 		trigger_events[group->element_resolved[l]][k] = TRUE;
3316 	    }
3317 	    else if (trigger_element == EL_ANY_ELEMENT)
3318 	      for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3319 		trigger_events[l][k] = TRUE;
3320 	    else
3321 	      trigger_events[trigger_element][k] = TRUE;
3322 	  }
3323 	}
3324       }
3325     }
3326   }
3327 
3328   /* ---------- initialize push delay -------------------------------------- */
3329 
3330   /* initialize push delay values to default */
3331   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3332   {
3333     if (!IS_CUSTOM_ELEMENT(i))
3334     {
3335       /* set default push delay values (corrected since version 3.0.7-1) */
3336       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3337       {
3338 	element_info[i].push_delay_fixed = 2;
3339 	element_info[i].push_delay_random = 8;
3340       }
3341       else
3342       {
3343 	element_info[i].push_delay_fixed = 8;
3344 	element_info[i].push_delay_random = 8;
3345       }
3346     }
3347   }
3348 
3349   /* set push delay value for certain elements from pre-defined list */
3350   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3351   {
3352     int e = push_delay_list[i].element;
3353 
3354     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3355     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3356   }
3357 
3358   /* set push delay value for Supaplex elements for newer engine versions */
3359   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3360   {
3361     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362     {
3363       if (IS_SP_ELEMENT(i))
3364       {
3365 	/* set SP push delay to just enough to push under a falling zonk */
3366 	int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3367 
3368 	element_info[i].push_delay_fixed  = delay;
3369 	element_info[i].push_delay_random = 0;
3370       }
3371     }
3372   }
3373 
3374   /* ---------- initialize move stepsize ----------------------------------- */
3375 
3376   /* initialize move stepsize values to default */
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378     if (!IS_CUSTOM_ELEMENT(i))
3379       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3380 
3381   /* set move stepsize value for certain elements from pre-defined list */
3382   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3383   {
3384     int e = move_stepsize_list[i].element;
3385 
3386     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3387   }
3388 
3389   /* ---------- initialize collect score ----------------------------------- */
3390 
3391   /* initialize collect score values for custom elements from initial value */
3392   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3393     if (IS_CUSTOM_ELEMENT(i))
3394       element_info[i].collect_score = element_info[i].collect_score_initial;
3395 
3396   /* ---------- initialize collect count ----------------------------------- */
3397 
3398   /* initialize collect count values for non-custom elements */
3399   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3400     if (!IS_CUSTOM_ELEMENT(i))
3401       element_info[i].collect_count_initial = 0;
3402 
3403   /* add collect count values for all elements from pre-defined list */
3404   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3405     element_info[collect_count_list[i].element].collect_count_initial =
3406       collect_count_list[i].count;
3407 
3408   /* ---------- initialize access direction -------------------------------- */
3409 
3410   /* initialize access direction values to default (access from every side) */
3411   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412     if (!IS_CUSTOM_ELEMENT(i))
3413       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3414 
3415   /* set access direction value for certain elements from pre-defined list */
3416   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3417     element_info[access_direction_list[i].element].access_direction =
3418       access_direction_list[i].direction;
3419 
3420   /* ---------- initialize explosion content ------------------------------- */
3421   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3422   {
3423     if (IS_CUSTOM_ELEMENT(i))
3424       continue;
3425 
3426     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3427     {
3428       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3429 
3430       element_info[i].content.e[x][y] =
3431 	(i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3432 	 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3433 	 i == EL_PLAYER_3 ? EL_EMERALD :
3434 	 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3435 	 i == EL_MOLE ? EL_EMERALD_RED :
3436 	 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3437 	 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3438 	 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3439 	 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3440 	 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3441 	 i == EL_WALL_EMERALD ? EL_EMERALD :
3442 	 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3443 	 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3444 	 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3445 	 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3446 	 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3447 	 i == EL_WALL_PEARL ? EL_PEARL :
3448 	 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3449 	 EL_EMPTY);
3450     }
3451   }
3452 
3453   /* ---------- initialize recursion detection ------------------------------ */
3454   recursion_loop_depth = 0;
3455   recursion_loop_detected = FALSE;
3456   recursion_loop_element = EL_UNDEFINED;
3457 
3458   /* ---------- initialize graphics engine ---------------------------------- */
3459   game.scroll_delay_value =
3460     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464 }
3465 
get_num_special_action(int element,int action_first,int action_last)3466 int get_num_special_action(int element, int action_first, int action_last)
3467 {
3468   int num_special_action = 0;
3469   int i, j;
3470 
3471   for (i = action_first; i <= action_last; i++)
3472   {
3473     boolean found = FALSE;
3474 
3475     for (j = 0; j < NUM_DIRECTIONS; j++)
3476       if (el_act_dir2img(element, i, j) !=
3477 	  el_act_dir2img(element, ACTION_DEFAULT, j))
3478 	found = TRUE;
3479 
3480     if (found)
3481       num_special_action++;
3482     else
3483       break;
3484   }
3485 
3486   return num_special_action;
3487 }
3488 
3489 
3490 /*
3491   =============================================================================
3492   InitGame()
3493   -----------------------------------------------------------------------------
3494   initialize and start new game
3495   =============================================================================
3496 */
3497 
InitGame()3498 void InitGame()
3499 {
3500   boolean emulate_bd = TRUE;	/* unless non-BOULDERDASH elements found */
3501   boolean emulate_sb = TRUE;	/* unless non-SOKOBAN     elements found */
3502   boolean emulate_sp = TRUE;	/* unless non-SUPAPLEX    elements found */
3503 #if 0
3504   boolean do_fading = (game_status == GAME_MODE_MAIN);
3505 #endif
3506 #if 1
3507   int initial_move_dir = MV_DOWN;
3508 #else
3509   int initial_move_dir = MV_NONE;
3510 #endif
3511   int i, j, x, y;
3512 
3513   game_status = GAME_MODE_PLAYING;
3514 
3515 #if 1
3516   /* needed if different viewport properties defined for playing */
3517   ChangeViewportPropertiesIfNeeded();
3518 #endif
3519 
3520 #if 1
3521   DrawCompleteVideoDisplay();
3522 #endif
3523 
3524   InitGameEngine();
3525   InitGameControlValues();
3526 
3527   /* don't play tapes over network */
3528   network_playing = (options.network && !tape.playing);
3529 
3530   for (i = 0; i < MAX_PLAYERS; i++)
3531   {
3532     struct PlayerInfo *player = &stored_player[i];
3533 
3534     player->index_nr = i;
3535     player->index_bit = (1 << i);
3536     player->element_nr = EL_PLAYER_1 + i;
3537 
3538     player->present = FALSE;
3539     player->active = FALSE;
3540     player->mapped = FALSE;
3541 
3542     player->killed = FALSE;
3543     player->reanimated = FALSE;
3544 
3545     player->action = 0;
3546     player->effective_action = 0;
3547     player->programmed_action = 0;
3548 
3549     player->score = 0;
3550     player->score_final = 0;
3551 
3552     player->gems_still_needed = level.gems_needed;
3553     player->sokobanfields_still_needed = 0;
3554     player->lights_still_needed = 0;
3555     player->friends_still_needed = 0;
3556 
3557     for (j = 0; j < MAX_NUM_KEYS; j++)
3558       player->key[j] = FALSE;
3559 
3560     player->num_white_keys = 0;
3561 
3562     player->dynabomb_count = 0;
3563     player->dynabomb_size = 1;
3564     player->dynabombs_left = 0;
3565     player->dynabomb_xl = FALSE;
3566 
3567     player->MovDir = initial_move_dir;
3568     player->MovPos = 0;
3569     player->GfxPos = 0;
3570     player->GfxDir = initial_move_dir;
3571     player->GfxAction = ACTION_DEFAULT;
3572     player->Frame = 0;
3573     player->StepFrame = 0;
3574 
3575     player->initial_element = player->element_nr;
3576     player->artwork_element =
3577       (level.use_artwork_element[i] ? level.artwork_element[i] :
3578        player->element_nr);
3579     player->use_murphy = FALSE;
3580 
3581     player->block_last_field = FALSE;	/* initialized in InitPlayerField() */
3582     player->block_delay_adjustment = 0;	/* initialized in InitPlayerField() */
3583 
3584     player->gravity = level.initial_player_gravity[i];
3585 
3586     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3587 
3588     player->actual_frame_counter = 0;
3589 
3590     player->step_counter = 0;
3591 
3592     player->last_move_dir = initial_move_dir;
3593 
3594     player->is_active = FALSE;
3595 
3596     player->is_waiting = FALSE;
3597     player->is_moving = FALSE;
3598     player->is_auto_moving = FALSE;
3599     player->is_digging = FALSE;
3600     player->is_snapping = FALSE;
3601     player->is_collecting = FALSE;
3602     player->is_pushing = FALSE;
3603     player->is_switching = FALSE;
3604     player->is_dropping = FALSE;
3605     player->is_dropping_pressed = FALSE;
3606 
3607     player->is_bored = FALSE;
3608     player->is_sleeping = FALSE;
3609 
3610     player->frame_counter_bored = -1;
3611     player->frame_counter_sleeping = -1;
3612 
3613     player->anim_delay_counter = 0;
3614     player->post_delay_counter = 0;
3615 
3616     player->dir_waiting = initial_move_dir;
3617     player->action_waiting = ACTION_DEFAULT;
3618     player->last_action_waiting = ACTION_DEFAULT;
3619     player->special_action_bored = ACTION_DEFAULT;
3620     player->special_action_sleeping = ACTION_DEFAULT;
3621 
3622     player->switch_x = -1;
3623     player->switch_y = -1;
3624 
3625     player->drop_x = -1;
3626     player->drop_y = -1;
3627 
3628     player->show_envelope = 0;
3629 
3630     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3631 
3632     player->push_delay       = -1;	/* initialized when pushing starts */
3633     player->push_delay_value = game.initial_push_delay_value;
3634 
3635     player->drop_delay = 0;
3636     player->drop_pressed_delay = 0;
3637 
3638     player->last_jx = -1;
3639     player->last_jy = -1;
3640     player->jx = -1;
3641     player->jy = -1;
3642 
3643     player->shield_normal_time_left = 0;
3644     player->shield_deadly_time_left = 0;
3645 
3646     player->inventory_infinite_element = EL_UNDEFINED;
3647     player->inventory_size = 0;
3648 
3649     if (level.use_initial_inventory[i])
3650     {
3651       for (j = 0; j < level.initial_inventory_size[i]; j++)
3652       {
3653 	int element = level.initial_inventory_content[i][j];
3654 	int collect_count = element_info[element].collect_count_initial;
3655 	int k;
3656 
3657 	if (!IS_CUSTOM_ELEMENT(element))
3658 	  collect_count = 1;
3659 
3660 	if (collect_count == 0)
3661 	  player->inventory_infinite_element = element;
3662 	else
3663 	  for (k = 0; k < collect_count; k++)
3664 	    if (player->inventory_size < MAX_INVENTORY_SIZE)
3665 	      player->inventory_element[player->inventory_size++] = element;
3666       }
3667     }
3668 
3669     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3670     SnapField(player, 0, 0);
3671 
3672     player->LevelSolved = FALSE;
3673     player->GameOver = FALSE;
3674 
3675     player->LevelSolved_GameWon = FALSE;
3676     player->LevelSolved_GameEnd = FALSE;
3677     player->LevelSolved_PanelOff = FALSE;
3678     player->LevelSolved_SaveTape = FALSE;
3679     player->LevelSolved_SaveScore = FALSE;
3680     player->LevelSolved_CountingTime = 0;
3681     player->LevelSolved_CountingScore = 0;
3682 
3683     map_player_action[i] = i;
3684   }
3685 
3686   network_player_action_received = FALSE;
3687 
3688 #if defined(NETWORK_AVALIABLE)
3689   /* initial null action */
3690   if (network_playing)
3691     SendToServer_MovePlayer(MV_NONE);
3692 #endif
3693 
3694   ZX = ZY = -1;
3695   ExitX = ExitY = -1;
3696 
3697   FrameCounter = 0;
3698   TimeFrames = 0;
3699   TimePlayed = 0;
3700   TimeLeft = level.time;
3701   TapeTime = 0;
3702 
3703   ScreenMovDir = MV_NONE;
3704   ScreenMovPos = 0;
3705   ScreenGfxPos = 0;
3706 
3707   ScrollStepSize = 0;	/* will be correctly initialized by ScrollScreen() */
3708 
3709   AllPlayersGone = FALSE;
3710 
3711   game.no_time_limit = (level.time == 0);
3712 
3713   game.yamyam_content_nr = 0;
3714   game.robot_wheel_active = FALSE;
3715   game.magic_wall_active = FALSE;
3716   game.magic_wall_time_left = 0;
3717   game.light_time_left = 0;
3718   game.timegate_time_left = 0;
3719   game.switchgate_pos = 0;
3720   game.wind_direction = level.wind_direction_initial;
3721 
3722 #if !USE_PLAYER_GRAVITY
3723   game.gravity = FALSE;
3724   game.explosions_delayed = TRUE;
3725 #endif
3726 
3727   game.lenses_time_left = 0;
3728   game.magnify_time_left = 0;
3729 
3730   game.ball_state = level.ball_state_initial;
3731   game.ball_content_nr = 0;
3732 
3733   game.envelope_active = FALSE;
3734 
3735   /* set focus to local player for network games, else to all players */
3736   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3737   game.centered_player_nr_next = game.centered_player_nr;
3738   game.set_centered_player = FALSE;
3739 
3740   if (network_playing && tape.recording)
3741   {
3742     /* store client dependent player focus when recording network games */
3743     tape.centered_player_nr_next = game.centered_player_nr_next;
3744     tape.set_centered_player = TRUE;
3745   }
3746 
3747   for (i = 0; i < NUM_BELTS; i++)
3748   {
3749     game.belt_dir[i] = MV_NONE;
3750     game.belt_dir_nr[i] = 3;		/* not moving, next moving left */
3751   }
3752 
3753   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3754     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3755 
3756   SCAN_PLAYFIELD(x, y)
3757   {
3758     Feld[x][y] = level.field[x][y];
3759     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3760     ChangeDelay[x][y] = 0;
3761     ChangePage[x][y] = -1;
3762 #if USE_NEW_CUSTOM_VALUE
3763     CustomValue[x][y] = 0;		/* initialized in InitField() */
3764 #endif
3765     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3766     AmoebaNr[x][y] = 0;
3767     WasJustMoving[x][y] = 0;
3768     WasJustFalling[x][y] = 0;
3769     CheckCollision[x][y] = 0;
3770     CheckImpact[x][y] = 0;
3771     Stop[x][y] = FALSE;
3772     Pushed[x][y] = FALSE;
3773 
3774     ChangeCount[x][y] = 0;
3775     ChangeEvent[x][y] = -1;
3776 
3777     ExplodePhase[x][y] = 0;
3778     ExplodeDelay[x][y] = 0;
3779     ExplodeField[x][y] = EX_TYPE_NONE;
3780 
3781     RunnerVisit[x][y] = 0;
3782     PlayerVisit[x][y] = 0;
3783 
3784     GfxFrame[x][y] = 0;
3785     GfxRandom[x][y] = INIT_GFX_RANDOM();
3786     GfxElement[x][y] = EL_UNDEFINED;
3787     GfxAction[x][y] = ACTION_DEFAULT;
3788     GfxDir[x][y] = MV_NONE;
3789     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3790   }
3791 
3792   SCAN_PLAYFIELD(x, y)
3793   {
3794     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3795       emulate_bd = FALSE;
3796     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3797       emulate_sb = FALSE;
3798     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3799       emulate_sp = FALSE;
3800 
3801     InitField(x, y, TRUE);
3802 
3803     ResetGfxAnimation(x, y);
3804   }
3805 
3806   InitBeltMovement();
3807 
3808   for (i = 0; i < MAX_PLAYERS; i++)
3809   {
3810     struct PlayerInfo *player = &stored_player[i];
3811 
3812     /* set number of special actions for bored and sleeping animation */
3813     player->num_special_action_bored =
3814       get_num_special_action(player->artwork_element,
3815 			     ACTION_BORING_1, ACTION_BORING_LAST);
3816     player->num_special_action_sleeping =
3817       get_num_special_action(player->artwork_element,
3818 			     ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3819   }
3820 
3821   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3822 		    emulate_sb ? EMU_SOKOBAN :
3823 		    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3824 
3825 #if USE_NEW_ALL_SLIPPERY
3826   /* initialize type of slippery elements */
3827   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3828   {
3829     if (!IS_CUSTOM_ELEMENT(i))
3830     {
3831       /* default: elements slip down either to the left or right randomly */
3832       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3833 
3834       /* SP style elements prefer to slip down on the left side */
3835       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3836 	element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3837 
3838       /* BD style elements prefer to slip down on the left side */
3839       if (game.emulation == EMU_BOULDERDASH)
3840 	element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3841     }
3842   }
3843 #endif
3844 
3845   /* initialize explosion and ignition delay */
3846   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3847   {
3848     if (!IS_CUSTOM_ELEMENT(i))
3849     {
3850       int num_phase = 8;
3851       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3852 		    game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3853 		   game.emulation == EMU_SUPAPLEX ? 3 : 2);
3854       int last_phase = (num_phase + 1) * delay;
3855       int half_phase = (num_phase / 2) * delay;
3856 
3857       element_info[i].explosion_delay = last_phase - 1;
3858       element_info[i].ignition_delay = half_phase;
3859 
3860       if (i == EL_BLACK_ORB)
3861 	element_info[i].ignition_delay = 1;
3862     }
3863 
3864 #if 0
3865     if (element_info[i].explosion_delay < 1)	/* !!! check again !!! */
3866       element_info[i].explosion_delay = 1;
3867 
3868     if (element_info[i].ignition_delay < 1)	/* !!! check again !!! */
3869       element_info[i].ignition_delay = 1;
3870 #endif
3871   }
3872 
3873   /* correct non-moving belts to start moving left */
3874   for (i = 0; i < NUM_BELTS; i++)
3875     if (game.belt_dir[i] == MV_NONE)
3876       game.belt_dir_nr[i] = 3;		/* not moving, next moving left */
3877 
3878 #if USE_NEW_PLAYER_ASSIGNMENTS
3879   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3880   /* choose default local player */
3881   local_player = &stored_player[0];
3882 
3883   for (i = 0; i < MAX_PLAYERS; i++)
3884     stored_player[i].connected = FALSE;
3885 
3886   local_player->connected = TRUE;
3887   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3888 
3889   if (tape.playing)
3890   {
3891     /* try to guess locally connected team mode players (needed for correct
3892        assignment of player figures from level to locally playing players) */
3893 
3894     for (i = 0; i < MAX_PLAYERS; i++)
3895       if (tape.player_participates[i])
3896 	stored_player[i].connected = TRUE;
3897   }
3898   else if (setup.team_mode && !options.network)
3899   {
3900     /* try to guess locally connected team mode players (needed for correct
3901        assignment of player figures from level to locally playing players) */
3902 
3903     for (i = 0; i < MAX_PLAYERS; i++)
3904       if (setup.input[i].use_joystick ||
3905 	  setup.input[i].key.left != KSYM_UNDEFINED)
3906 	stored_player[i].connected = TRUE;
3907   }
3908 
3909 #if 0
3910   for (i = 0; i < MAX_PLAYERS; i++)
3911     printf("::: player %d: %s\n", i,
3912 	   (stored_player[i].connected ? "connected" : "not connected"));
3913 
3914   for (i = 0; i < MAX_PLAYERS; i++)
3915     printf("::: player %d: %s\n", i,
3916 	   (stored_player[i].present ? "present" : "not present"));
3917 #endif
3918 
3919   /* check if any connected player was not found in playfield */
3920   for (i = 0; i < MAX_PLAYERS; i++)
3921   {
3922     struct PlayerInfo *player = &stored_player[i];
3923 
3924     if (player->connected && !player->present)
3925     {
3926       struct PlayerInfo *field_player = NULL;
3927 
3928 #if 0
3929       printf("::: looking for field player for player %d ...\n", i);
3930 #endif
3931 
3932       /* assign first free player found that is present in the playfield */
3933 
3934       /* first try: look for unmapped playfield player that is not connected */
3935       if (field_player == NULL)
3936 	for (j = 0; j < MAX_PLAYERS; j++)
3937 	  if (stored_player[j].present &&
3938 	      !stored_player[j].mapped &&
3939 	      !stored_player[j].connected)
3940 	    field_player = &stored_player[j];
3941 
3942       /* second try: look for *any* unmapped playfield player */
3943       if (field_player == NULL)
3944 	for (j = 0; j < MAX_PLAYERS; j++)
3945 	  if (stored_player[j].present &&
3946 	      !stored_player[j].mapped)
3947 	    field_player = &stored_player[j];
3948 
3949       if (field_player != NULL)
3950       {
3951 	int jx = field_player->jx, jy = field_player->jy;
3952 
3953 #if 0
3954 	printf("::: found player figure %d\n", field_player->index_nr);
3955 #endif
3956 
3957 	player->present = FALSE;
3958 	player->active = FALSE;
3959 
3960 	field_player->present = TRUE;
3961 	field_player->active = TRUE;
3962 
3963 	/*
3964 	player->initial_element = field_player->initial_element;
3965 	player->artwork_element = field_player->artwork_element;
3966 
3967 	player->block_last_field       = field_player->block_last_field;
3968 	player->block_delay_adjustment = field_player->block_delay_adjustment;
3969 	*/
3970 
3971 	StorePlayer[jx][jy] = field_player->element_nr;
3972 
3973 	field_player->jx = field_player->last_jx = jx;
3974 	field_player->jy = field_player->last_jy = jy;
3975 
3976 	if (local_player == player)
3977 	  local_player = field_player;
3978 
3979 	map_player_action[field_player->index_nr] = i;
3980 
3981 	field_player->mapped = TRUE;
3982 
3983 #if 0
3984 	printf("::: map_player_action[%d] == %d\n",
3985 	       field_player->index_nr, i);
3986 #endif
3987       }
3988     }
3989 
3990     if (player->connected && player->present)
3991       player->mapped = TRUE;
3992   }
3993 
3994 #else
3995 
3996   /* check if any connected player was not found in playfield */
3997   for (i = 0; i < MAX_PLAYERS; i++)
3998   {
3999     struct PlayerInfo *player = &stored_player[i];
4000 
4001     if (player->connected && !player->present)
4002     {
4003       for (j = 0; j < MAX_PLAYERS; j++)
4004       {
4005 	struct PlayerInfo *field_player = &stored_player[j];
4006 	int jx = field_player->jx, jy = field_player->jy;
4007 
4008 	/* assign first free player found that is present in the playfield */
4009 	if (field_player->present && !field_player->connected)
4010 	{
4011 	  player->present = TRUE;
4012 	  player->active = TRUE;
4013 
4014 	  field_player->present = FALSE;
4015 	  field_player->active = FALSE;
4016 
4017 	  player->initial_element = field_player->initial_element;
4018 	  player->artwork_element = field_player->artwork_element;
4019 
4020 	  player->block_last_field       = field_player->block_last_field;
4021 	  player->block_delay_adjustment = field_player->block_delay_adjustment;
4022 
4023 	  StorePlayer[jx][jy] = player->element_nr;
4024 
4025 	  player->jx = player->last_jx = jx;
4026 	  player->jy = player->last_jy = jy;
4027 
4028 	  break;
4029 	}
4030       }
4031     }
4032   }
4033 #endif
4034 
4035 #if 0
4036   printf("::: local_player->present == %d\n", local_player->present);
4037 #endif
4038 
4039   if (tape.playing)
4040   {
4041     /* when playing a tape, eliminate all players who do not participate */
4042 
4043 #if USE_NEW_PLAYER_ASSIGNMENTS
4044     for (i = 0; i < MAX_PLAYERS; i++)
4045     {
4046       if (stored_player[i].active &&
4047 	  !tape.player_participates[map_player_action[i]])
4048       {
4049 	struct PlayerInfo *player = &stored_player[i];
4050 	int jx = player->jx, jy = player->jy;
4051 
4052 	player->active = FALSE;
4053 	StorePlayer[jx][jy] = 0;
4054 	Feld[jx][jy] = EL_EMPTY;
4055       }
4056     }
4057 #else
4058     for (i = 0; i < MAX_PLAYERS; i++)
4059     {
4060       if (stored_player[i].active &&
4061 	  !tape.player_participates[i])
4062       {
4063 	struct PlayerInfo *player = &stored_player[i];
4064 	int jx = player->jx, jy = player->jy;
4065 
4066 	player->active = FALSE;
4067 	StorePlayer[jx][jy] = 0;
4068 	Feld[jx][jy] = EL_EMPTY;
4069       }
4070     }
4071 #endif
4072   }
4073   else if (!options.network && !setup.team_mode)	/* && !tape.playing */
4074   {
4075     /* when in single player mode, eliminate all but the first active player */
4076 
4077     for (i = 0; i < MAX_PLAYERS; i++)
4078     {
4079       if (stored_player[i].active)
4080       {
4081 	for (j = i + 1; j < MAX_PLAYERS; j++)
4082 	{
4083 	  if (stored_player[j].active)
4084 	  {
4085 	    struct PlayerInfo *player = &stored_player[j];
4086 	    int jx = player->jx, jy = player->jy;
4087 
4088 	    player->active = FALSE;
4089 	    player->present = FALSE;
4090 
4091 	    StorePlayer[jx][jy] = 0;
4092 	    Feld[jx][jy] = EL_EMPTY;
4093 	  }
4094 	}
4095       }
4096     }
4097   }
4098 
4099   /* when recording the game, store which players take part in the game */
4100   if (tape.recording)
4101   {
4102 #if USE_NEW_PLAYER_ASSIGNMENTS
4103     for (i = 0; i < MAX_PLAYERS; i++)
4104       if (stored_player[i].connected)
4105 	tape.player_participates[i] = TRUE;
4106 #else
4107     for (i = 0; i < MAX_PLAYERS; i++)
4108       if (stored_player[i].active)
4109 	tape.player_participates[i] = TRUE;
4110 #endif
4111   }
4112 
4113   if (options.debug)
4114   {
4115     for (i = 0; i < MAX_PLAYERS; i++)
4116     {
4117       struct PlayerInfo *player = &stored_player[i];
4118 
4119       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4120 	     i+1,
4121 	     player->present,
4122 	     player->connected,
4123 	     player->active);
4124       if (local_player == player)
4125 	printf("Player 	%d is local player.\n", i+1);
4126     }
4127   }
4128 
4129   if (BorderElement == EL_EMPTY)
4130   {
4131     SBX_Left = 0;
4132     SBX_Right = lev_fieldx - SCR_FIELDX;
4133     SBY_Upper = 0;
4134     SBY_Lower = lev_fieldy - SCR_FIELDY;
4135   }
4136   else
4137   {
4138     SBX_Left = -1;
4139     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4140     SBY_Upper = -1;
4141     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4142   }
4143 
4144 #if NEW_TILESIZE
4145 
4146   if (lev_fieldx + (SBX_Left < 0 ? 2 : 0) <= SCR_FIELDX)
4147     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4148 
4149   if (lev_fieldy + (SBY_Upper < 0 ? 2 : 0) <= SCR_FIELDY)
4150     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4151 
4152   if (EVEN(SCR_FIELDX))
4153     SBX_Left--;
4154   if (EVEN(SCR_FIELDY))
4155     SBY_Upper--;
4156 
4157 #else
4158 
4159   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4160     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4161 
4162   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4163     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4164 #endif
4165 
4166   /* if local player not found, look for custom element that might create
4167      the player (make some assumptions about the right custom element) */
4168   if (!local_player->present)
4169   {
4170     int start_x = 0, start_y = 0;
4171     int found_rating = 0;
4172     int found_element = EL_UNDEFINED;
4173     int player_nr = local_player->index_nr;
4174 
4175     SCAN_PLAYFIELD(x, y)
4176     {
4177       int element = Feld[x][y];
4178       int content;
4179       int xx, yy;
4180       boolean is_player;
4181 
4182       if (level.use_start_element[player_nr] &&
4183 	  level.start_element[player_nr] == element &&
4184 	  found_rating < 4)
4185       {
4186 	start_x = x;
4187 	start_y = y;
4188 
4189 	found_rating = 4;
4190 	found_element = element;
4191       }
4192 
4193       if (!IS_CUSTOM_ELEMENT(element))
4194 	continue;
4195 
4196       if (CAN_CHANGE(element))
4197       {
4198 	for (i = 0; i < element_info[element].num_change_pages; i++)
4199 	{
4200 	  /* check for player created from custom element as single target */
4201 	  content = element_info[element].change_page[i].target_element;
4202 	  is_player = ELEM_IS_PLAYER(content);
4203 
4204 	  if (is_player && (found_rating < 3 ||
4205 			    (found_rating == 3 && element < found_element)))
4206 	  {
4207 	    start_x = x;
4208 	    start_y = y;
4209 
4210 	    found_rating = 3;
4211 	    found_element = element;
4212 	  }
4213 	}
4214       }
4215 
4216       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4217       {
4218 	/* check for player created from custom element as explosion content */
4219 	content = element_info[element].content.e[xx][yy];
4220 	is_player = ELEM_IS_PLAYER(content);
4221 
4222 	if (is_player && (found_rating < 2 ||
4223 			  (found_rating == 2 && element < found_element)))
4224 	{
4225 	  start_x = x + xx - 1;
4226 	  start_y = y + yy - 1;
4227 
4228 	  found_rating = 2;
4229 	  found_element = element;
4230 	}
4231 
4232 	if (!CAN_CHANGE(element))
4233 	  continue;
4234 
4235 	for (i = 0; i < element_info[element].num_change_pages; i++)
4236 	{
4237 	  /* check for player created from custom element as extended target */
4238 	  content =
4239 	    element_info[element].change_page[i].target_content.e[xx][yy];
4240 
4241 	  is_player = ELEM_IS_PLAYER(content);
4242 
4243 	  if (is_player && (found_rating < 1 ||
4244 			    (found_rating == 1 && element < found_element)))
4245 	  {
4246 	    start_x = x + xx - 1;
4247 	    start_y = y + yy - 1;
4248 
4249 	    found_rating = 1;
4250 	    found_element = element;
4251 	  }
4252 	}
4253       }
4254     }
4255 
4256     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4257 		start_x > SBX_Right + MIDPOSX ? SBX_Right :
4258 		start_x - MIDPOSX);
4259 
4260     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4261 		start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4262 		start_y - MIDPOSY);
4263   }
4264   else
4265   {
4266     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4267 		local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4268 		local_player->jx - MIDPOSX);
4269 
4270     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4271 		local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4272 		local_player->jy - MIDPOSY);
4273   }
4274 
4275 #if 0
4276   printf("::: %d, %d (initial)\n", scroll_x, scroll_y);
4277 #endif
4278 
4279 #if 0
4280   /* do not use PLAYING mask for fading out from main screen */
4281   game_status = GAME_MODE_MAIN;
4282 #endif
4283 
4284   StopAnimation();
4285 
4286   if (!game.restart_level)
4287     CloseDoor(DOOR_CLOSE_1);
4288 
4289 #if 1
4290   if (level_editor_test_game)
4291     FadeSkipNextFadeIn();
4292   else
4293     FadeSetEnterScreen();
4294 #else
4295   if (level_editor_test_game)
4296     fading = fading_none;
4297   else
4298     fading = menu.destination;
4299 #endif
4300 
4301 #if 1
4302   FadeOut(REDRAW_FIELD);
4303 #else
4304   if (do_fading)
4305     FadeOut(REDRAW_FIELD);
4306 #endif
4307 
4308 #if 0
4309   game_status = GAME_MODE_PLAYING;
4310 #endif
4311 
4312   /* !!! FIX THIS (START) !!! */
4313   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4314   {
4315     InitGameEngine_EM();
4316 
4317     /* blit playfield from scroll buffer to normal back buffer for fading in */
4318     BlitScreenToBitmap_EM(backbuffer);
4319   }
4320   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4321   {
4322     InitGameEngine_SP();
4323 
4324     /* blit playfield from scroll buffer to normal back buffer for fading in */
4325     BlitScreenToBitmap_SP(backbuffer);
4326   }
4327   else
4328   {
4329     DrawLevel();
4330     DrawAllPlayers();
4331 
4332     /* after drawing the level, correct some elements */
4333     if (game.timegate_time_left == 0)
4334       CloseAllOpenTimegates();
4335 
4336 #if NEW_TILESIZE
4337     BlitScreenToBitmap(backbuffer);
4338 #else
4339     /* blit playfield from scroll buffer to normal back buffer for fading in */
4340     if (setup.soft_scrolling)
4341       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4342 #endif
4343 
4344     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4345   }
4346   /* !!! FIX THIS (END) !!! */
4347 
4348 #if 1
4349   FadeIn(REDRAW_FIELD);
4350 #else
4351   if (do_fading)
4352     FadeIn(REDRAW_FIELD);
4353 
4354   BackToFront();
4355 #endif
4356 
4357   if (!game.restart_level)
4358   {
4359     /* copy default game door content to main double buffer */
4360 #if 1
4361 #if 1
4362     /* !!! CHECK AGAIN !!! */
4363     SetPanelBackground();
4364     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4365     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4366 #else
4367     struct GraphicInfo *gfx = &graphic_info[IMG_BACKGROUND_PANEL];
4368 
4369     /* (ClearRectangle() only needed if panel bitmap is smaller than panel) */
4370     ClearRectangle(drawto, DX, DY, DXSIZE, DYSIZE);
4371     BlitBitmap(gfx->bitmap, drawto, gfx->src_x, gfx->src_y,
4372 	       MIN(gfx->width, DXSIZE), MIN(gfx->height, DYSIZE), DX, DY);
4373 #endif
4374 #else
4375     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4376 	       DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4377 #endif
4378   }
4379 
4380   SetPanelBackground();
4381   SetDrawBackgroundMask(REDRAW_DOOR_1);
4382 
4383 #if 1
4384   UpdateAndDisplayGameControlValues();
4385 #else
4386   UpdateGameDoorValues();
4387   DrawGameDoorValues();
4388 #endif
4389 
4390   if (!game.restart_level)
4391   {
4392     UnmapGameButtons();
4393     UnmapTapeButtons();
4394     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4395     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4396     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4397     MapGameButtons();
4398     MapTapeButtons();
4399 
4400     /* copy actual game door content to door double buffer for OpenDoor() */
4401     BlitBitmap(drawto, bitmap_db_door,
4402 	       DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4403 
4404     OpenDoor(DOOR_OPEN_ALL);
4405 
4406     PlaySound(SND_GAME_STARTING);
4407 
4408     if (setup.sound_music)
4409       PlayLevelMusic();
4410 
4411     KeyboardAutoRepeatOffUnlessAutoplay();
4412 
4413     if (options.debug)
4414     {
4415       for (i = 0; i < MAX_PLAYERS; i++)
4416 	printf("Player %d %sactive.\n",
4417 	       i + 1, (stored_player[i].active ? "" : "not "));
4418     }
4419   }
4420 
4421 #if 1
4422   UnmapAllGadgets();
4423 
4424   MapGameButtons();
4425   MapTapeButtons();
4426 #endif
4427 
4428   if (!game.restart_level && !tape.playing)
4429   {
4430     LevelStats_incPlayed(level_nr);
4431 
4432     SaveLevelSetup_SeriesInfo();
4433 
4434 #if 0
4435     printf("::: PLAYING LEVEL (%d)\n", LevelStats_getPlayed(level_nr));
4436 #endif
4437   }
4438 
4439   game.restart_level = FALSE;
4440 }
4441 
UpdateEngineValues(int actual_scroll_x,int actual_scroll_y)4442 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4443 {
4444   /* this is used for non-R'n'D game engines to update certain engine values */
4445 
4446   /* needed to determine if sounds are played within the visible screen area */
4447   scroll_x = actual_scroll_x;
4448   scroll_y = actual_scroll_y;
4449 }
4450 
InitMovDir(int x,int y)4451 void InitMovDir(int x, int y)
4452 {
4453   int i, element = Feld[x][y];
4454   static int xy[4][2] =
4455   {
4456     {  0, +1 },
4457     { +1,  0 },
4458     {  0, -1 },
4459     { -1,  0 }
4460   };
4461   static int direction[3][4] =
4462   {
4463     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4464     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4465     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4466   };
4467 
4468   switch (element)
4469   {
4470     case EL_BUG_RIGHT:
4471     case EL_BUG_UP:
4472     case EL_BUG_LEFT:
4473     case EL_BUG_DOWN:
4474       Feld[x][y] = EL_BUG;
4475       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4476       break;
4477 
4478     case EL_SPACESHIP_RIGHT:
4479     case EL_SPACESHIP_UP:
4480     case EL_SPACESHIP_LEFT:
4481     case EL_SPACESHIP_DOWN:
4482       Feld[x][y] = EL_SPACESHIP;
4483       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4484       break;
4485 
4486     case EL_BD_BUTTERFLY_RIGHT:
4487     case EL_BD_BUTTERFLY_UP:
4488     case EL_BD_BUTTERFLY_LEFT:
4489     case EL_BD_BUTTERFLY_DOWN:
4490       Feld[x][y] = EL_BD_BUTTERFLY;
4491       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4492       break;
4493 
4494     case EL_BD_FIREFLY_RIGHT:
4495     case EL_BD_FIREFLY_UP:
4496     case EL_BD_FIREFLY_LEFT:
4497     case EL_BD_FIREFLY_DOWN:
4498       Feld[x][y] = EL_BD_FIREFLY;
4499       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4500       break;
4501 
4502     case EL_PACMAN_RIGHT:
4503     case EL_PACMAN_UP:
4504     case EL_PACMAN_LEFT:
4505     case EL_PACMAN_DOWN:
4506       Feld[x][y] = EL_PACMAN;
4507       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4508       break;
4509 
4510     case EL_YAMYAM_LEFT:
4511     case EL_YAMYAM_RIGHT:
4512     case EL_YAMYAM_UP:
4513     case EL_YAMYAM_DOWN:
4514       Feld[x][y] = EL_YAMYAM;
4515       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4516       break;
4517 
4518     case EL_SP_SNIKSNAK:
4519       MovDir[x][y] = MV_UP;
4520       break;
4521 
4522     case EL_SP_ELECTRON:
4523       MovDir[x][y] = MV_LEFT;
4524       break;
4525 
4526     case EL_MOLE_LEFT:
4527     case EL_MOLE_RIGHT:
4528     case EL_MOLE_UP:
4529     case EL_MOLE_DOWN:
4530       Feld[x][y] = EL_MOLE;
4531       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4532       break;
4533 
4534     default:
4535       if (IS_CUSTOM_ELEMENT(element))
4536       {
4537 	struct ElementInfo *ei = &element_info[element];
4538 	int move_direction_initial = ei->move_direction_initial;
4539 	int move_pattern = ei->move_pattern;
4540 
4541 	if (move_direction_initial == MV_START_PREVIOUS)
4542 	{
4543 	  if (MovDir[x][y] != MV_NONE)
4544 	    return;
4545 
4546 	  move_direction_initial = MV_START_AUTOMATIC;
4547 	}
4548 
4549 	if (move_direction_initial == MV_START_RANDOM)
4550 	  MovDir[x][y] = 1 << RND(4);
4551 	else if (move_direction_initial & MV_ANY_DIRECTION)
4552 	  MovDir[x][y] = move_direction_initial;
4553 	else if (move_pattern == MV_ALL_DIRECTIONS ||
4554 		 move_pattern == MV_TURNING_LEFT ||
4555 		 move_pattern == MV_TURNING_RIGHT ||
4556 		 move_pattern == MV_TURNING_LEFT_RIGHT ||
4557 		 move_pattern == MV_TURNING_RIGHT_LEFT ||
4558 		 move_pattern == MV_TURNING_RANDOM)
4559 	  MovDir[x][y] = 1 << RND(4);
4560 	else if (move_pattern == MV_HORIZONTAL)
4561 	  MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4562 	else if (move_pattern == MV_VERTICAL)
4563 	  MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4564 	else if (move_pattern & MV_ANY_DIRECTION)
4565 	  MovDir[x][y] = element_info[element].move_pattern;
4566 	else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4567 		 move_pattern == MV_ALONG_RIGHT_SIDE)
4568 	{
4569 	  /* use random direction as default start direction */
4570 	  if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4571 	    MovDir[x][y] = 1 << RND(4);
4572 
4573 	  for (i = 0; i < NUM_DIRECTIONS; i++)
4574 	  {
4575 	    int x1 = x + xy[i][0];
4576 	    int y1 = y + xy[i][1];
4577 
4578 	    if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4579 	    {
4580 	      if (move_pattern == MV_ALONG_RIGHT_SIDE)
4581 		MovDir[x][y] = direction[0][i];
4582 	      else
4583 		MovDir[x][y] = direction[1][i];
4584 
4585 	      break;
4586 	    }
4587 	  }
4588 	}
4589       }
4590       else
4591       {
4592 	MovDir[x][y] = 1 << RND(4);
4593 
4594 	if (element != EL_BUG &&
4595 	    element != EL_SPACESHIP &&
4596 	    element != EL_BD_BUTTERFLY &&
4597 	    element != EL_BD_FIREFLY)
4598 	  break;
4599 
4600 	for (i = 0; i < NUM_DIRECTIONS; i++)
4601 	{
4602 	  int x1 = x + xy[i][0];
4603 	  int y1 = y + xy[i][1];
4604 
4605 	  if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4606 	  {
4607 	    if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4608 	    {
4609 	      MovDir[x][y] = direction[0][i];
4610 	      break;
4611 	    }
4612 	    else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4613 		     element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4614 	    {
4615 	      MovDir[x][y] = direction[1][i];
4616 	      break;
4617 	    }
4618 	  }
4619 	}
4620       }
4621       break;
4622   }
4623 
4624   GfxDir[x][y] = MovDir[x][y];
4625 }
4626 
InitAmoebaNr(int x,int y)4627 void InitAmoebaNr(int x, int y)
4628 {
4629   int i;
4630   int group_nr = AmoebeNachbarNr(x, y);
4631 
4632   if (group_nr == 0)
4633   {
4634     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4635     {
4636       if (AmoebaCnt[i] == 0)
4637       {
4638 	group_nr = i;
4639 	break;
4640       }
4641     }
4642   }
4643 
4644   AmoebaNr[x][y] = group_nr;
4645   AmoebaCnt[group_nr]++;
4646   AmoebaCnt2[group_nr]++;
4647 }
4648 
PlayerWins(struct PlayerInfo * player)4649 static void PlayerWins(struct PlayerInfo *player)
4650 {
4651   player->LevelSolved = TRUE;
4652   player->GameOver = TRUE;
4653 
4654   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4655 			 level.native_em_level->lev->score : player->score);
4656 
4657   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4658 				      TimeLeft);
4659   player->LevelSolved_CountingScore = player->score_final;
4660 }
4661 
GameWon()4662 void GameWon()
4663 {
4664   static int time, time_final;
4665   static int score, score_final;
4666   static int game_over_delay_1 = 0;
4667   static int game_over_delay_2 = 0;
4668   int game_over_delay_value_1 = 50;
4669   int game_over_delay_value_2 = 50;
4670 
4671   if (!local_player->LevelSolved_GameWon)
4672   {
4673     int i;
4674 
4675     /* do not start end game actions before the player stops moving (to exit) */
4676     if (local_player->MovPos)
4677       return;
4678 
4679     local_player->LevelSolved_GameWon = TRUE;
4680     local_player->LevelSolved_SaveTape = tape.recording;
4681     local_player->LevelSolved_SaveScore = !tape.playing;
4682 
4683     if (!tape.playing)
4684     {
4685       LevelStats_incSolved(level_nr);
4686 
4687       SaveLevelSetup_SeriesInfo();
4688 
4689 #if 0
4690       printf("::: LEVEL SOLVED (%d)\n", LevelStats_getSolved(level_nr));
4691 #endif
4692     }
4693 
4694     if (tape.auto_play)		/* tape might already be stopped here */
4695       tape.auto_play_level_solved = TRUE;
4696 
4697 #if 1
4698     TapeStop();
4699 #endif
4700 
4701     game_over_delay_1 = game_over_delay_value_1;
4702     game_over_delay_2 = game_over_delay_value_2;
4703 
4704     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4705     score = score_final = local_player->score_final;
4706 
4707     if (TimeLeft > 0)
4708     {
4709       time_final = 0;
4710       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4711     }
4712     else if (game.no_time_limit && TimePlayed < 999)
4713     {
4714       time_final = 999;
4715       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4716     }
4717 
4718     local_player->score_final = score_final;
4719 
4720     if (level_editor_test_game)
4721     {
4722       time = time_final;
4723       score = score_final;
4724 
4725 #if 1
4726       local_player->LevelSolved_CountingTime = time;
4727       local_player->LevelSolved_CountingScore = score;
4728 
4729       game_panel_controls[GAME_PANEL_TIME].value = time;
4730       game_panel_controls[GAME_PANEL_SCORE].value = score;
4731 
4732       DisplayGameControlValues();
4733 #else
4734       DrawGameValue_Time(time);
4735       DrawGameValue_Score(score);
4736 #endif
4737     }
4738 
4739     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4740     {
4741       if (ExitX >= 0 && ExitY >= 0)	/* local player has left the level */
4742       {
4743 	/* close exit door after last player */
4744 	if ((AllPlayersGone &&
4745 	     (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4746 	      Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4747 	      Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4748 	    Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4749 	    Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4750 	{
4751 	  int element = Feld[ExitX][ExitY];
4752 
4753 #if 0
4754 	  if (element == EL_EM_EXIT_OPEN ||
4755 	      element == EL_EM_STEEL_EXIT_OPEN)
4756 	  {
4757 	    Bang(ExitX, ExitY);
4758 	  }
4759 	  else
4760 #endif
4761 	  {
4762 	    Feld[ExitX][ExitY] =
4763 	      (element == EL_EXIT_OPEN		? EL_EXIT_CLOSING :
4764 	       element == EL_EM_EXIT_OPEN	? EL_EM_EXIT_CLOSING :
4765 	       element == EL_SP_EXIT_OPEN	? EL_SP_EXIT_CLOSING:
4766 	       element == EL_STEEL_EXIT_OPEN	? EL_STEEL_EXIT_CLOSING:
4767 	       EL_EM_STEEL_EXIT_CLOSING);
4768 
4769 	    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4770 	  }
4771 	}
4772 
4773 	/* player disappears */
4774 	DrawLevelField(ExitX, ExitY);
4775       }
4776 
4777       for (i = 0; i < MAX_PLAYERS; i++)
4778       {
4779 	struct PlayerInfo *player = &stored_player[i];
4780 
4781 	if (player->present)
4782 	{
4783 	  RemovePlayer(player);
4784 
4785 	  /* player disappears */
4786 	  DrawLevelField(player->jx, player->jy);
4787 	}
4788       }
4789     }
4790 
4791     PlaySound(SND_GAME_WINNING);
4792   }
4793 
4794   if (game_over_delay_1 > 0)
4795   {
4796     game_over_delay_1--;
4797 
4798     return;
4799   }
4800 
4801   if (time != time_final)
4802   {
4803     int time_to_go = ABS(time_final - time);
4804     int time_count_dir = (time < time_final ? +1 : -1);
4805     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4806 
4807     time  += time_count_steps * time_count_dir;
4808     score += time_count_steps * level.score[SC_TIME_BONUS];
4809 
4810 #if 1
4811     local_player->LevelSolved_CountingTime = time;
4812     local_player->LevelSolved_CountingScore = score;
4813 
4814     game_panel_controls[GAME_PANEL_TIME].value = time;
4815     game_panel_controls[GAME_PANEL_SCORE].value = score;
4816 
4817     DisplayGameControlValues();
4818 #else
4819     DrawGameValue_Time(time);
4820     DrawGameValue_Score(score);
4821 #endif
4822 
4823     if (time == time_final)
4824       StopSound(SND_GAME_LEVELTIME_BONUS);
4825     else if (setup.sound_loops)
4826       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4827     else
4828       PlaySound(SND_GAME_LEVELTIME_BONUS);
4829 
4830     return;
4831   }
4832 
4833   local_player->LevelSolved_PanelOff = TRUE;
4834 
4835   if (game_over_delay_2 > 0)
4836   {
4837     game_over_delay_2--;
4838 
4839     return;
4840   }
4841 
4842 #if 1
4843   GameEnd();
4844 #endif
4845 }
4846 
GameEnd()4847 void GameEnd()
4848 {
4849   int hi_pos;
4850   boolean raise_level = FALSE;
4851 
4852   local_player->LevelSolved_GameEnd = TRUE;
4853 
4854   CloseDoor(DOOR_CLOSE_1);
4855 
4856   if (local_player->LevelSolved_SaveTape)
4857   {
4858 #if 0
4859     TapeStop();
4860 #endif
4861 
4862 #if 1
4863     SaveTapeChecked(tape.level_nr);	/* ask to save tape */
4864 #else
4865     SaveTape(tape.level_nr);		/* ask to save tape */
4866 #endif
4867   }
4868 
4869   if (level_editor_test_game)
4870   {
4871     game_status = GAME_MODE_MAIN;
4872 
4873 #if 1
4874     DrawAndFadeInMainMenu(REDRAW_FIELD);
4875 #else
4876     DrawMainMenu();
4877 #endif
4878 
4879     return;
4880   }
4881 
4882   if (!local_player->LevelSolved_SaveScore)
4883   {
4884 #if 1
4885     FadeOut(REDRAW_FIELD);
4886 #endif
4887 
4888     game_status = GAME_MODE_MAIN;
4889 
4890     DrawAndFadeInMainMenu(REDRAW_FIELD);
4891 
4892     return;
4893   }
4894 
4895   if (level_nr == leveldir_current->handicap_level)
4896   {
4897     leveldir_current->handicap_level++;
4898 
4899     SaveLevelSetup_SeriesInfo();
4900   }
4901 
4902   if (level_nr < leveldir_current->last_level)
4903     raise_level = TRUE;			/* advance to next level */
4904 
4905   if ((hi_pos = NewHiScore()) >= 0)
4906   {
4907     game_status = GAME_MODE_SCORES;
4908 
4909     DrawHallOfFame(hi_pos);
4910 
4911     if (raise_level)
4912     {
4913       level_nr++;
4914       TapeErase();
4915     }
4916   }
4917   else
4918   {
4919 #if 1
4920     FadeOut(REDRAW_FIELD);
4921 #endif
4922 
4923     game_status = GAME_MODE_MAIN;
4924 
4925     if (raise_level)
4926     {
4927       level_nr++;
4928       TapeErase();
4929     }
4930 
4931     DrawAndFadeInMainMenu(REDRAW_FIELD);
4932   }
4933 }
4934 
NewHiScore()4935 int NewHiScore()
4936 {
4937   int k, l;
4938   int position = -1;
4939 
4940   LoadScore(level_nr);
4941 
4942   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4943       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
4944     return -1;
4945 
4946   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
4947   {
4948     if (local_player->score_final > highscore[k].Score)
4949     {
4950       /* player has made it to the hall of fame */
4951 
4952       if (k < MAX_SCORE_ENTRIES - 1)
4953       {
4954 	int m = MAX_SCORE_ENTRIES - 1;
4955 
4956 #ifdef ONE_PER_NAME
4957 	for (l = k; l < MAX_SCORE_ENTRIES; l++)
4958 	  if (strEqual(setup.player_name, highscore[l].Name))
4959 	    m = l;
4960 	if (m == k)	/* player's new highscore overwrites his old one */
4961 	  goto put_into_list;
4962 #endif
4963 
4964 	for (l = m; l > k; l--)
4965 	{
4966 	  strcpy(highscore[l].Name, highscore[l - 1].Name);
4967 	  highscore[l].Score = highscore[l - 1].Score;
4968 	}
4969       }
4970 
4971 #ifdef ONE_PER_NAME
4972       put_into_list:
4973 #endif
4974       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4975       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4976       highscore[k].Score = local_player->score_final;
4977       position = k;
4978       break;
4979     }
4980 
4981 #ifdef ONE_PER_NAME
4982     else if (!strncmp(setup.player_name, highscore[k].Name,
4983 		      MAX_PLAYER_NAME_LEN))
4984       break;	/* player already there with a higher score */
4985 #endif
4986 
4987   }
4988 
4989   if (position >= 0)
4990     SaveScore(level_nr);
4991 
4992   return position;
4993 }
4994 
getElementMoveStepsizeExt(int x,int y,int direction)4995 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4996 {
4997   int element = Feld[x][y];
4998   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4999   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5000   int horiz_move = (dx != 0);
5001   int sign = (horiz_move ? dx : dy);
5002   int step = sign * element_info[element].move_stepsize;
5003 
5004   /* special values for move stepsize for spring and things on conveyor belt */
5005   if (horiz_move)
5006   {
5007     if (CAN_FALL(element) &&
5008 	y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5009       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5010     else if (element == EL_SPRING)
5011       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5012   }
5013 
5014   return step;
5015 }
5016 
getElementMoveStepsize(int x,int y)5017 inline static int getElementMoveStepsize(int x, int y)
5018 {
5019   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5020 }
5021 
InitPlayerGfxAnimation(struct PlayerInfo * player,int action,int dir)5022 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5023 {
5024   if (player->GfxAction != action || player->GfxDir != dir)
5025   {
5026 #if 0
5027     printf("Player frame reset! (%d => %d, %d => %d)\n",
5028 	   player->GfxAction, action, player->GfxDir, dir);
5029 #endif
5030 
5031     player->GfxAction = action;
5032     player->GfxDir = dir;
5033     player->Frame = 0;
5034     player->StepFrame = 0;
5035   }
5036 }
5037 
5038 #if USE_GFX_RESET_GFX_ANIMATION
ResetGfxFrame(int x,int y,boolean redraw)5039 static void ResetGfxFrame(int x, int y, boolean redraw)
5040 {
5041   int element = Feld[x][y];
5042   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5043   int last_gfx_frame = GfxFrame[x][y];
5044 
5045   if (graphic_info[graphic].anim_global_sync)
5046     GfxFrame[x][y] = FrameCounter;
5047   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5048     GfxFrame[x][y] = CustomValue[x][y];
5049   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5050     GfxFrame[x][y] = element_info[element].collect_score;
5051   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5052     GfxFrame[x][y] = ChangeDelay[x][y];
5053 
5054   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5055     DrawLevelGraphicAnimation(x, y, graphic);
5056 }
5057 #endif
5058 
ResetGfxAnimation(int x,int y)5059 static void ResetGfxAnimation(int x, int y)
5060 {
5061   GfxAction[x][y] = ACTION_DEFAULT;
5062   GfxDir[x][y] = MovDir[x][y];
5063   GfxFrame[x][y] = 0;
5064 
5065 #if USE_GFX_RESET_GFX_ANIMATION
5066   ResetGfxFrame(x, y, FALSE);
5067 #endif
5068 }
5069 
ResetRandomAnimationValue(int x,int y)5070 static void ResetRandomAnimationValue(int x, int y)
5071 {
5072   GfxRandom[x][y] = INIT_GFX_RANDOM();
5073 }
5074 
InitMovingField(int x,int y,int direction)5075 void InitMovingField(int x, int y, int direction)
5076 {
5077   int element = Feld[x][y];
5078   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5079   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5080   int newx = x + dx;
5081   int newy = y + dy;
5082   boolean is_moving_before, is_moving_after;
5083 #if 0
5084   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5085 #endif
5086 
5087   /* check if element was/is moving or being moved before/after mode change */
5088 #if 1
5089 #if 1
5090   is_moving_before = (WasJustMoving[x][y] != 0);
5091 #else
5092   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5093   is_moving_before = WasJustMoving[x][y];
5094 #endif
5095 #else
5096   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5097 #endif
5098   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5099 
5100   /* reset animation only for moving elements which change direction of moving
5101      or which just started or stopped moving
5102      (else CEs with property "can move" / "not moving" are reset each frame) */
5103 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5104 #if 1
5105   if (is_moving_before != is_moving_after ||
5106       direction != MovDir[x][y])
5107     ResetGfxAnimation(x, y);
5108 #else
5109   if ((is_moving_before || is_moving_after) && !continues_moving)
5110     ResetGfxAnimation(x, y);
5111 #endif
5112 #else
5113   if (!continues_moving)
5114     ResetGfxAnimation(x, y);
5115 #endif
5116 
5117   MovDir[x][y] = direction;
5118   GfxDir[x][y] = direction;
5119 
5120 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5121   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5122 		     direction == MV_DOWN && CAN_FALL(element) ?
5123 		     ACTION_FALLING : ACTION_MOVING);
5124 #else
5125   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5126 		     ACTION_FALLING : ACTION_MOVING);
5127 #endif
5128 
5129   /* this is needed for CEs with property "can move" / "not moving" */
5130 
5131   if (is_moving_after)
5132   {
5133     if (Feld[newx][newy] == EL_EMPTY)
5134       Feld[newx][newy] = EL_BLOCKED;
5135 
5136     MovDir[newx][newy] = MovDir[x][y];
5137 
5138 #if USE_NEW_CUSTOM_VALUE
5139     CustomValue[newx][newy] = CustomValue[x][y];
5140 #endif
5141 
5142     GfxFrame[newx][newy] = GfxFrame[x][y];
5143     GfxRandom[newx][newy] = GfxRandom[x][y];
5144     GfxAction[newx][newy] = GfxAction[x][y];
5145     GfxDir[newx][newy] = GfxDir[x][y];
5146   }
5147 }
5148 
Moving2Blocked(int x,int y,int * goes_to_x,int * goes_to_y)5149 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5150 {
5151   int direction = MovDir[x][y];
5152   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5153   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5154 
5155   *goes_to_x = newx;
5156   *goes_to_y = newy;
5157 }
5158 
Blocked2Moving(int x,int y,int * comes_from_x,int * comes_from_y)5159 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5160 {
5161   int oldx = x, oldy = y;
5162   int direction = MovDir[x][y];
5163 
5164   if (direction == MV_LEFT)
5165     oldx++;
5166   else if (direction == MV_RIGHT)
5167     oldx--;
5168   else if (direction == MV_UP)
5169     oldy++;
5170   else if (direction == MV_DOWN)
5171     oldy--;
5172 
5173   *comes_from_x = oldx;
5174   *comes_from_y = oldy;
5175 }
5176 
MovingOrBlocked2Element(int x,int y)5177 int MovingOrBlocked2Element(int x, int y)
5178 {
5179   int element = Feld[x][y];
5180 
5181   if (element == EL_BLOCKED)
5182   {
5183     int oldx, oldy;
5184 
5185     Blocked2Moving(x, y, &oldx, &oldy);
5186     return Feld[oldx][oldy];
5187   }
5188   else
5189     return element;
5190 }
5191 
MovingOrBlocked2ElementIfNotLeaving(int x,int y)5192 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5193 {
5194   /* like MovingOrBlocked2Element(), but if element is moving
5195      and (x,y) is the field the moving element is just leaving,
5196      return EL_BLOCKED instead of the element value */
5197   int element = Feld[x][y];
5198 
5199   if (IS_MOVING(x, y))
5200   {
5201     if (element == EL_BLOCKED)
5202     {
5203       int oldx, oldy;
5204 
5205       Blocked2Moving(x, y, &oldx, &oldy);
5206       return Feld[oldx][oldy];
5207     }
5208     else
5209       return EL_BLOCKED;
5210   }
5211   else
5212     return element;
5213 }
5214 
RemoveField(int x,int y)5215 static void RemoveField(int x, int y)
5216 {
5217   Feld[x][y] = EL_EMPTY;
5218 
5219   MovPos[x][y] = 0;
5220   MovDir[x][y] = 0;
5221   MovDelay[x][y] = 0;
5222 
5223 #if USE_NEW_CUSTOM_VALUE
5224   CustomValue[x][y] = 0;
5225 #endif
5226 
5227   AmoebaNr[x][y] = 0;
5228   ChangeDelay[x][y] = 0;
5229   ChangePage[x][y] = -1;
5230   Pushed[x][y] = FALSE;
5231 
5232 #if 0
5233   ExplodeField[x][y] = EX_TYPE_NONE;
5234 #endif
5235 
5236   GfxElement[x][y] = EL_UNDEFINED;
5237   GfxAction[x][y] = ACTION_DEFAULT;
5238   GfxDir[x][y] = MV_NONE;
5239 #if 0
5240   /* !!! this would prevent the removed tile from being redrawn !!! */
5241   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5242 #endif
5243 }
5244 
RemoveMovingField(int x,int y)5245 void RemoveMovingField(int x, int y)
5246 {
5247   int oldx = x, oldy = y, newx = x, newy = y;
5248   int element = Feld[x][y];
5249   int next_element = EL_UNDEFINED;
5250 
5251   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5252     return;
5253 
5254   if (IS_MOVING(x, y))
5255   {
5256     Moving2Blocked(x, y, &newx, &newy);
5257 
5258     if (Feld[newx][newy] != EL_BLOCKED)
5259     {
5260       /* element is moving, but target field is not free (blocked), but
5261 	 already occupied by something different (example: acid pool);
5262 	 in this case, only remove the moving field, but not the target */
5263 
5264       RemoveField(oldx, oldy);
5265 
5266       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5267 
5268       TEST_DrawLevelField(oldx, oldy);
5269 
5270       return;
5271     }
5272   }
5273   else if (element == EL_BLOCKED)
5274   {
5275     Blocked2Moving(x, y, &oldx, &oldy);
5276     if (!IS_MOVING(oldx, oldy))
5277       return;
5278   }
5279 
5280   if (element == EL_BLOCKED &&
5281       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5282        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5283        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5284        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5285        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5286        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5287     next_element = get_next_element(Feld[oldx][oldy]);
5288 
5289   RemoveField(oldx, oldy);
5290   RemoveField(newx, newy);
5291 
5292   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5293 
5294   if (next_element != EL_UNDEFINED)
5295     Feld[oldx][oldy] = next_element;
5296 
5297   TEST_DrawLevelField(oldx, oldy);
5298   TEST_DrawLevelField(newx, newy);
5299 }
5300 
DrawDynamite(int x,int y)5301 void DrawDynamite(int x, int y)
5302 {
5303   int sx = SCREENX(x), sy = SCREENY(y);
5304   int graphic = el2img(Feld[x][y]);
5305   int frame;
5306 
5307   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5308     return;
5309 
5310   if (IS_WALKABLE_INSIDE(Back[x][y]))
5311     return;
5312 
5313   if (Back[x][y])
5314     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5315   else if (Store[x][y])
5316     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5317 
5318   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5319 
5320   if (Back[x][y] || Store[x][y])
5321     DrawGraphicThruMask(sx, sy, graphic, frame);
5322   else
5323     DrawGraphic(sx, sy, graphic, frame);
5324 }
5325 
CheckDynamite(int x,int y)5326 void CheckDynamite(int x, int y)
5327 {
5328   if (MovDelay[x][y] != 0)	/* dynamite is still waiting to explode */
5329   {
5330     MovDelay[x][y]--;
5331 
5332     if (MovDelay[x][y] != 0)
5333     {
5334       DrawDynamite(x, y);
5335       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5336 
5337       return;
5338     }
5339   }
5340 
5341   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5342 
5343   Bang(x, y);
5344 }
5345 
setMinimalPlayerBoundaries(int * sx1,int * sy1,int * sx2,int * sy2)5346 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5347 {
5348   boolean num_checked_players = 0;
5349   int i;
5350 
5351   for (i = 0; i < MAX_PLAYERS; i++)
5352   {
5353     if (stored_player[i].active)
5354     {
5355       int sx = stored_player[i].jx;
5356       int sy = stored_player[i].jy;
5357 
5358       if (num_checked_players == 0)
5359       {
5360 	*sx1 = *sx2 = sx;
5361 	*sy1 = *sy2 = sy;
5362       }
5363       else
5364       {
5365 	*sx1 = MIN(*sx1, sx);
5366 	*sy1 = MIN(*sy1, sy);
5367 	*sx2 = MAX(*sx2, sx);
5368 	*sy2 = MAX(*sy2, sy);
5369       }
5370 
5371       num_checked_players++;
5372     }
5373   }
5374 }
5375 
checkIfAllPlayersFitToScreen_RND()5376 static boolean checkIfAllPlayersFitToScreen_RND()
5377 {
5378   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5379 
5380   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5381 
5382   return (sx2 - sx1 < SCR_FIELDX &&
5383 	  sy2 - sy1 < SCR_FIELDY);
5384 }
5385 
setScreenCenteredToAllPlayers(int * sx,int * sy)5386 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5387 {
5388   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5389 
5390   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5391 
5392   *sx = (sx1 + sx2) / 2;
5393   *sy = (sy1 + sy2) / 2;
5394 }
5395 
DrawRelocateScreen(int old_x,int old_y,int x,int y,int move_dir,boolean center_screen,boolean quick_relocation)5396 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5397 			boolean center_screen, boolean quick_relocation)
5398 {
5399   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5400   boolean no_delay = (tape.warp_forward);
5401   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5402   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5403 
5404   if (quick_relocation)
5405   {
5406     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5407     {
5408       if (!level.shifted_relocation || center_screen)
5409       {
5410 	/* quick relocation (without scrolling), with centering of screen */
5411 
5412 	scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5413 		    x > SBX_Right + MIDPOSX ? SBX_Right :
5414 		    x - MIDPOSX);
5415 
5416 	scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5417 		    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5418 		    y - MIDPOSY);
5419       }
5420       else
5421       {
5422 	/* quick relocation (without scrolling), but do not center screen */
5423 
5424 	int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5425 			       old_x > SBX_Right + MIDPOSX ? SBX_Right :
5426 			       old_x - MIDPOSX);
5427 
5428 	int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5429 			       old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5430 			       old_y - MIDPOSY);
5431 
5432 	int offset_x = x + (scroll_x - center_scroll_x);
5433 	int offset_y = y + (scroll_y - center_scroll_y);
5434 
5435 	scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5436 		    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5437 		    offset_x - MIDPOSX);
5438 
5439 	scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5440 		    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5441 		    offset_y - MIDPOSY);
5442       }
5443     }
5444     else
5445     {
5446 #if 1
5447       if (!level.shifted_relocation || center_screen)
5448       {
5449 	/* quick relocation (without scrolling), with centering of screen */
5450 
5451 	scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5452 		    x > SBX_Right + MIDPOSX ? SBX_Right :
5453 		    x - MIDPOSX);
5454 
5455 	scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5456 		    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5457 		    y - MIDPOSY);
5458       }
5459       else
5460       {
5461 	/* quick relocation (without scrolling), but do not center screen */
5462 
5463 	int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5464 			       old_x > SBX_Right + MIDPOSX ? SBX_Right :
5465 			       old_x - MIDPOSX);
5466 
5467 	int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5468 			       old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5469 			       old_y - MIDPOSY);
5470 
5471 	int offset_x = x + (scroll_x - center_scroll_x);
5472 	int offset_y = y + (scroll_y - center_scroll_y);
5473 
5474 	scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5475 		    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5476 		    offset_x - MIDPOSX);
5477 
5478 	scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5479 		    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5480 		    offset_y - MIDPOSY);
5481       }
5482 #else
5483       /* quick relocation (without scrolling), inside visible screen area */
5484 
5485       int offset = game.scroll_delay_value;
5486 
5487       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5488 	  (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5489 	scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5490 
5491       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5492 	  (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5493 	scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5494 
5495       /* don't scroll over playfield boundaries */
5496       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5497 	scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5498 
5499       /* don't scroll over playfield boundaries */
5500       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5501 	scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5502 #endif
5503     }
5504 
5505     RedrawPlayfield(TRUE, 0,0,0,0);
5506   }
5507   else
5508   {
5509 #if 1
5510     int scroll_xx, scroll_yy;
5511 
5512     if (!level.shifted_relocation || center_screen)
5513     {
5514       /* visible relocation (with scrolling), with centering of screen */
5515 
5516       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5517 		   x > SBX_Right + MIDPOSX ? SBX_Right :
5518 		   x - MIDPOSX);
5519 
5520       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5521 		   y > SBY_Lower + MIDPOSY ? SBY_Lower :
5522 		   y - MIDPOSY);
5523     }
5524     else
5525     {
5526       /* visible relocation (with scrolling), but do not center screen */
5527 
5528       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5529 			     old_x > SBX_Right + MIDPOSX ? SBX_Right :
5530 			     old_x - MIDPOSX);
5531 
5532       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5533 			     old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5534 			     old_y - MIDPOSY);
5535 
5536       int offset_x = x + (scroll_x - center_scroll_x);
5537       int offset_y = y + (scroll_y - center_scroll_y);
5538 
5539       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5540 		   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5541 		   offset_x - MIDPOSX);
5542 
5543       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5544 		   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5545 		   offset_y - MIDPOSY);
5546     }
5547 
5548 #else
5549 
5550     /* visible relocation (with scrolling), with centering of screen */
5551 
5552     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5553 		     x > SBX_Right + MIDPOSX ? SBX_Right :
5554 		     x - MIDPOSX);
5555 
5556     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5557 		     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5558 		     y - MIDPOSY);
5559 #endif
5560 
5561     ScrollScreen(NULL, SCROLL_GO_ON);	/* scroll last frame to full tile */
5562 
5563     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5564     {
5565       int dx = 0, dy = 0;
5566       int fx = FX, fy = FY;
5567 
5568       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5569       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5570 
5571       if (dx == 0 && dy == 0)		/* no scrolling needed at all */
5572 	break;
5573 
5574       scroll_x -= dx;
5575       scroll_y -= dy;
5576 
5577       fx += dx * TILEX / 2;
5578       fy += dy * TILEY / 2;
5579 
5580       ScrollLevel(dx, dy);
5581       DrawAllPlayers();
5582 
5583       /* scroll in two steps of half tile size to make things smoother */
5584       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5585       FlushDisplay();
5586       Delay(wait_delay_value);
5587 
5588       /* scroll second step to align at full tile size */
5589       BackToFront();
5590       Delay(wait_delay_value);
5591     }
5592 
5593     DrawAllPlayers();
5594     BackToFront();
5595     Delay(wait_delay_value);
5596   }
5597 }
5598 
RelocatePlayer(int jx,int jy,int el_player_raw)5599 void RelocatePlayer(int jx, int jy, int el_player_raw)
5600 {
5601   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5602   int player_nr = GET_PLAYER_NR(el_player);
5603   struct PlayerInfo *player = &stored_player[player_nr];
5604   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5605   boolean no_delay = (tape.warp_forward);
5606   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5607   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5608   int old_jx = player->jx;
5609   int old_jy = player->jy;
5610   int old_element = Feld[old_jx][old_jy];
5611   int element = Feld[jx][jy];
5612   boolean player_relocated = (old_jx != jx || old_jy != jy);
5613 
5614   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5615   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5616   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5617   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5618   int leave_side_horiz = move_dir_horiz;
5619   int leave_side_vert  = move_dir_vert;
5620   int enter_side = enter_side_horiz | enter_side_vert;
5621   int leave_side = leave_side_horiz | leave_side_vert;
5622 
5623   if (player->GameOver)		/* do not reanimate dead player */
5624     return;
5625 
5626   if (!player_relocated)	/* no need to relocate the player */
5627     return;
5628 
5629   if (IS_PLAYER(jx, jy))	/* player already placed at new position */
5630   {
5631     RemoveField(jx, jy);	/* temporarily remove newly placed player */
5632     DrawLevelField(jx, jy);
5633   }
5634 
5635   if (player->present)
5636   {
5637     while (player->MovPos)
5638     {
5639       ScrollPlayer(player, SCROLL_GO_ON);
5640       ScrollScreen(NULL, SCROLL_GO_ON);
5641 
5642       AdvanceFrameAndPlayerCounters(player->index_nr);
5643 
5644       DrawPlayer(player);
5645 
5646       BackToFront();
5647       Delay(wait_delay_value);
5648     }
5649 
5650     DrawPlayer(player);		/* needed here only to cleanup last field */
5651     DrawLevelField(player->jx, player->jy);	/* remove player graphic */
5652 
5653     player->is_moving = FALSE;
5654   }
5655 
5656   if (IS_CUSTOM_ELEMENT(old_element))
5657     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5658 			       CE_LEFT_BY_PLAYER,
5659 			       player->index_bit, leave_side);
5660 
5661   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5662 				      CE_PLAYER_LEAVES_X,
5663 				      player->index_bit, leave_side);
5664 
5665   Feld[jx][jy] = el_player;
5666   InitPlayerField(jx, jy, el_player, TRUE);
5667 
5668   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5669      possible that the relocation target field did not contain a player element,
5670      but a walkable element, to which the new player was relocated -- in this
5671      case, restore that (already initialized!) element on the player field */
5672   if (!ELEM_IS_PLAYER(element))	/* player may be set on walkable element */
5673   {
5674     Feld[jx][jy] = element;	/* restore previously existing element */
5675 #if 0
5676     /* !!! do not initialize already initialized element a second time !!! */
5677     /* (this causes at least problems with "element creation" CE trigger for
5678        already existing elements, and existing Sokoban fields counted twice) */
5679     InitField(jx, jy, FALSE);
5680 #endif
5681   }
5682 
5683   /* only visually relocate centered player */
5684   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5685 		     FALSE, level.instant_relocation);
5686 
5687   TestIfPlayerTouchesBadThing(jx, jy);
5688   TestIfPlayerTouchesCustomElement(jx, jy);
5689 
5690   if (IS_CUSTOM_ELEMENT(element))
5691     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5692 			       player->index_bit, enter_side);
5693 
5694   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5695 				      player->index_bit, enter_side);
5696 
5697 #if 1
5698   if (player->is_switching)
5699   {
5700     /* ensure that relocation while still switching an element does not cause
5701        a new element to be treated as also switched directly after relocation
5702        (this is important for teleporter switches that teleport the player to
5703        a place where another teleporter switch is in the same direction, which
5704        would then incorrectly be treated as immediately switched before the
5705        direction key that caused the switch was released) */
5706 
5707     player->switch_x += jx - old_jx;
5708     player->switch_y += jy - old_jy;
5709   }
5710 #endif
5711 }
5712 
Explode(int ex,int ey,int phase,int mode)5713 void Explode(int ex, int ey, int phase, int mode)
5714 {
5715   int x, y;
5716   int last_phase;
5717   int border_element;
5718 
5719   /* !!! eliminate this variable !!! */
5720   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5721 
5722   if (game.explosions_delayed)
5723   {
5724     ExplodeField[ex][ey] = mode;
5725     return;
5726   }
5727 
5728   if (phase == EX_PHASE_START)		/* initialize 'Store[][]' field */
5729   {
5730     int center_element = Feld[ex][ey];
5731     int artwork_element, explosion_element;	/* set these values later */
5732 
5733 #if 0
5734     /* --- This is only really needed (and now handled) in "Impact()". --- */
5735     /* do not explode moving elements that left the explode field in time */
5736     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5737 	center_element == EL_EMPTY &&
5738 	(mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5739       return;
5740 #endif
5741 
5742 #if 0
5743     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5744     if (mode == EX_TYPE_NORMAL ||
5745 	mode == EX_TYPE_CENTER ||
5746 	mode == EX_TYPE_CROSS)
5747       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5748 #endif
5749 
5750     /* remove things displayed in background while burning dynamite */
5751     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5752       Back[ex][ey] = 0;
5753 
5754     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5755     {
5756       /* put moving element to center field (and let it explode there) */
5757       center_element = MovingOrBlocked2Element(ex, ey);
5758       RemoveMovingField(ex, ey);
5759       Feld[ex][ey] = center_element;
5760     }
5761 
5762     /* now "center_element" is finally determined -- set related values now */
5763     artwork_element = center_element;		/* for custom player artwork */
5764     explosion_element = center_element;		/* for custom player artwork */
5765 
5766     if (IS_PLAYER(ex, ey))
5767     {
5768       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5769 
5770       artwork_element = stored_player[player_nr].artwork_element;
5771 
5772       if (level.use_explosion_element[player_nr])
5773       {
5774 	explosion_element = level.explosion_element[player_nr];
5775 	artwork_element = explosion_element;
5776       }
5777     }
5778 
5779 #if 1
5780     if (mode == EX_TYPE_NORMAL ||
5781 	mode == EX_TYPE_CENTER ||
5782 	mode == EX_TYPE_CROSS)
5783       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5784 #endif
5785 
5786     last_phase = element_info[explosion_element].explosion_delay + 1;
5787 
5788     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5789     {
5790       int xx = x - ex + 1;
5791       int yy = y - ey + 1;
5792       int element;
5793 
5794       if (!IN_LEV_FIELD(x, y) ||
5795 	  (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5796 	  (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5797 	continue;
5798 
5799       element = Feld[x][y];
5800 
5801       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5802       {
5803 	element = MovingOrBlocked2Element(x, y);
5804 
5805 	if (!IS_EXPLOSION_PROOF(element))
5806 	  RemoveMovingField(x, y);
5807       }
5808 
5809       /* indestructible elements can only explode in center (but not flames) */
5810       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5811 					   mode == EX_TYPE_BORDER)) ||
5812 	  element == EL_FLAMES)
5813 	continue;
5814 
5815       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5816 	 behaviour, for example when touching a yamyam that explodes to rocks
5817 	 with active deadly shield, a rock is created under the player !!! */
5818       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5819 #if 0
5820       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5821 	  (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5822 	   (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5823 #else
5824       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5825 #endif
5826       {
5827 	if (IS_ACTIVE_BOMB(element))
5828 	{
5829 	  /* re-activate things under the bomb like gate or penguin */
5830 	  Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5831 	  Back[x][y] = 0;
5832 	}
5833 
5834 	continue;
5835       }
5836 
5837       /* save walkable background elements while explosion on same tile */
5838       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5839 	  (x != ex || y != ey || mode == EX_TYPE_BORDER))
5840 	Back[x][y] = element;
5841 
5842       /* ignite explodable elements reached by other explosion */
5843       if (element == EL_EXPLOSION)
5844 	element = Store2[x][y];
5845 
5846       if (AmoebaNr[x][y] &&
5847 	  (element == EL_AMOEBA_FULL ||
5848 	   element == EL_BD_AMOEBA ||
5849 	   element == EL_AMOEBA_GROWING))
5850       {
5851 	AmoebaCnt[AmoebaNr[x][y]]--;
5852 	AmoebaCnt2[AmoebaNr[x][y]]--;
5853       }
5854 
5855       RemoveField(x, y);
5856 
5857       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5858       {
5859 	int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5860 
5861 	Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5862 
5863 	if (PLAYERINFO(ex, ey)->use_murphy)
5864 	  Store[x][y] = EL_EMPTY;
5865       }
5866 
5867       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5868 	 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5869       else if (ELEM_IS_PLAYER(center_element))
5870 	Store[x][y] = EL_EMPTY;
5871       else if (center_element == EL_YAMYAM)
5872 	Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5873       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5874 	Store[x][y] = element_info[center_element].content.e[xx][yy];
5875 #if 1
5876       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5877 	 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5878 	 otherwise) -- FIX THIS !!! */
5879       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5880 	Store[x][y] = element_info[element].content.e[1][1];
5881 #else
5882       else if (!CAN_EXPLODE(element))
5883 	Store[x][y] = element_info[element].content.e[1][1];
5884 #endif
5885       else
5886 	Store[x][y] = EL_EMPTY;
5887 
5888       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5889 	  center_element == EL_AMOEBA_TO_DIAMOND)
5890 	Store2[x][y] = element;
5891 
5892       Feld[x][y] = EL_EXPLOSION;
5893       GfxElement[x][y] = artwork_element;
5894 
5895       ExplodePhase[x][y] = 1;
5896       ExplodeDelay[x][y] = last_phase;
5897 
5898       Stop[x][y] = TRUE;
5899     }
5900 
5901     if (center_element == EL_YAMYAM)
5902       game.yamyam_content_nr =
5903 	(game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5904 
5905     return;
5906   }
5907 
5908   if (Stop[ex][ey])
5909     return;
5910 
5911   x = ex;
5912   y = ey;
5913 
5914   if (phase == 1)
5915     GfxFrame[x][y] = 0;		/* restart explosion animation */
5916 
5917   last_phase = ExplodeDelay[x][y];
5918 
5919   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5920 
5921 #ifdef DEBUG
5922 
5923   /* activate this even in non-DEBUG version until cause for crash in
5924      getGraphicAnimationFrame() (see below) is found and eliminated */
5925 
5926 #endif
5927 #if 1
5928 
5929 #if 1
5930   /* this can happen if the player leaves an explosion just in time */
5931   if (GfxElement[x][y] == EL_UNDEFINED)
5932     GfxElement[x][y] = EL_EMPTY;
5933 #else
5934   if (GfxElement[x][y] == EL_UNDEFINED)
5935   {
5936     printf("\n\n");
5937     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
5938     printf("Explode(): This should never happen!\n");
5939     printf("\n\n");
5940 
5941     GfxElement[x][y] = EL_EMPTY;
5942   }
5943 #endif
5944 
5945 #endif
5946 
5947   border_element = Store2[x][y];
5948   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5949     border_element = StorePlayer[x][y];
5950 
5951   if (phase == element_info[border_element].ignition_delay ||
5952       phase == last_phase)
5953   {
5954     boolean border_explosion = FALSE;
5955 
5956     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5957 	!PLAYER_EXPLOSION_PROTECTED(x, y))
5958     {
5959       KillPlayerUnlessExplosionProtected(x, y);
5960       border_explosion = TRUE;
5961     }
5962     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5963     {
5964       Feld[x][y] = Store2[x][y];
5965       Store2[x][y] = 0;
5966       Bang(x, y);
5967       border_explosion = TRUE;
5968     }
5969     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5970     {
5971       AmoebeUmwandeln(x, y);
5972       Store2[x][y] = 0;
5973       border_explosion = TRUE;
5974     }
5975 
5976     /* if an element just explodes due to another explosion (chain-reaction),
5977        do not immediately end the new explosion when it was the last frame of
5978        the explosion (as it would be done in the following "if"-statement!) */
5979     if (border_explosion && phase == last_phase)
5980       return;
5981   }
5982 
5983   if (phase == last_phase)
5984   {
5985     int element;
5986 
5987     element = Feld[x][y] = Store[x][y];
5988     Store[x][y] = Store2[x][y] = 0;
5989     GfxElement[x][y] = EL_UNDEFINED;
5990 
5991     /* player can escape from explosions and might therefore be still alive */
5992     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5993 	element <= EL_PLAYER_IS_EXPLODING_4)
5994     {
5995       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5996       int explosion_element = EL_PLAYER_1 + player_nr;
5997       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5998       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5999 
6000       if (level.use_explosion_element[player_nr])
6001 	explosion_element = level.explosion_element[player_nr];
6002 
6003       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6004 		    element_info[explosion_element].content.e[xx][yy]);
6005     }
6006 
6007     /* restore probably existing indestructible background element */
6008     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6009       element = Feld[x][y] = Back[x][y];
6010     Back[x][y] = 0;
6011 
6012     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6013     GfxDir[x][y] = MV_NONE;
6014     ChangeDelay[x][y] = 0;
6015     ChangePage[x][y] = -1;
6016 
6017 #if USE_NEW_CUSTOM_VALUE
6018     CustomValue[x][y] = 0;
6019 #endif
6020 
6021     InitField_WithBug2(x, y, FALSE);
6022 
6023     TEST_DrawLevelField(x, y);
6024 
6025     TestIfElementTouchesCustomElement(x, y);
6026 
6027     if (GFX_CRUMBLED(element))
6028       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6029 
6030     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6031       StorePlayer[x][y] = 0;
6032 
6033     if (ELEM_IS_PLAYER(element))
6034       RelocatePlayer(x, y, element);
6035   }
6036   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6037   {
6038     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6039     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6040 
6041     if (phase == delay)
6042       TEST_DrawLevelFieldCrumbled(x, y);
6043 
6044     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6045     {
6046       DrawLevelElement(x, y, Back[x][y]);
6047       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6048     }
6049     else if (IS_WALKABLE_UNDER(Back[x][y]))
6050     {
6051       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6052       DrawLevelElementThruMask(x, y, Back[x][y]);
6053     }
6054     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6055       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6056   }
6057 }
6058 
DynaExplode(int ex,int ey)6059 void DynaExplode(int ex, int ey)
6060 {
6061   int i, j;
6062   int dynabomb_element = Feld[ex][ey];
6063   int dynabomb_size = 1;
6064   boolean dynabomb_xl = FALSE;
6065   struct PlayerInfo *player;
6066   static int xy[4][2] =
6067   {
6068     { 0, -1 },
6069     { -1, 0 },
6070     { +1, 0 },
6071     { 0, +1 }
6072   };
6073 
6074   if (IS_ACTIVE_BOMB(dynabomb_element))
6075   {
6076     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6077     dynabomb_size = player->dynabomb_size;
6078     dynabomb_xl = player->dynabomb_xl;
6079     player->dynabombs_left++;
6080   }
6081 
6082   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6083 
6084   for (i = 0; i < NUM_DIRECTIONS; i++)
6085   {
6086     for (j = 1; j <= dynabomb_size; j++)
6087     {
6088       int x = ex + j * xy[i][0];
6089       int y = ey + j * xy[i][1];
6090       int element;
6091 
6092       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6093 	break;
6094 
6095       element = Feld[x][y];
6096 
6097       /* do not restart explosions of fields with active bombs */
6098       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6099 	continue;
6100 
6101       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6102 
6103       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6104 	  !IS_DIGGABLE(element) && !dynabomb_xl)
6105 	break;
6106     }
6107   }
6108 }
6109 
Bang(int x,int y)6110 void Bang(int x, int y)
6111 {
6112   int element = MovingOrBlocked2Element(x, y);
6113   int explosion_type = EX_TYPE_NORMAL;
6114 
6115   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6116   {
6117     struct PlayerInfo *player = PLAYERINFO(x, y);
6118 
6119 #if USE_FIX_CE_ACTION_WITH_PLAYER
6120     element = Feld[x][y] = player->initial_element;
6121 #else
6122     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6123 			    player->element_nr);
6124 #endif
6125 
6126     if (level.use_explosion_element[player->index_nr])
6127     {
6128       int explosion_element = level.explosion_element[player->index_nr];
6129 
6130       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6131 	explosion_type = EX_TYPE_CROSS;
6132       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6133 	explosion_type = EX_TYPE_CENTER;
6134     }
6135   }
6136 
6137   switch (element)
6138   {
6139     case EL_BUG:
6140     case EL_SPACESHIP:
6141     case EL_BD_BUTTERFLY:
6142     case EL_BD_FIREFLY:
6143     case EL_YAMYAM:
6144     case EL_DARK_YAMYAM:
6145     case EL_ROBOT:
6146     case EL_PACMAN:
6147     case EL_MOLE:
6148       RaiseScoreElement(element);
6149       break;
6150 
6151     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6152     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6153     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6154     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6155     case EL_DYNABOMB_INCREASE_NUMBER:
6156     case EL_DYNABOMB_INCREASE_SIZE:
6157     case EL_DYNABOMB_INCREASE_POWER:
6158       explosion_type = EX_TYPE_DYNA;
6159       break;
6160 
6161     case EL_DC_LANDMINE:
6162 #if 0
6163     case EL_EM_EXIT_OPEN:
6164     case EL_EM_STEEL_EXIT_OPEN:
6165 #endif
6166       explosion_type = EX_TYPE_CENTER;
6167       break;
6168 
6169     case EL_PENGUIN:
6170     case EL_LAMP:
6171     case EL_LAMP_ACTIVE:
6172     case EL_AMOEBA_TO_DIAMOND:
6173       if (!IS_PLAYER(x, y))	/* penguin and player may be at same field */
6174 	explosion_type = EX_TYPE_CENTER;
6175       break;
6176 
6177     default:
6178       if (element_info[element].explosion_type == EXPLODES_CROSS)
6179 	explosion_type = EX_TYPE_CROSS;
6180       else if (element_info[element].explosion_type == EXPLODES_1X1)
6181 	explosion_type = EX_TYPE_CENTER;
6182       break;
6183   }
6184 
6185   if (explosion_type == EX_TYPE_DYNA)
6186     DynaExplode(x, y);
6187   else
6188     Explode(x, y, EX_PHASE_START, explosion_type);
6189 
6190   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6191 }
6192 
SplashAcid(int x,int y)6193 void SplashAcid(int x, int y)
6194 {
6195   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6196       (!IN_LEV_FIELD(x - 1, y - 2) ||
6197        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6198     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6199 
6200   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6201       (!IN_LEV_FIELD(x + 1, y - 2) ||
6202        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6203     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6204 
6205   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6206 }
6207 
InitBeltMovement()6208 static void InitBeltMovement()
6209 {
6210   static int belt_base_element[4] =
6211   {
6212     EL_CONVEYOR_BELT_1_LEFT,
6213     EL_CONVEYOR_BELT_2_LEFT,
6214     EL_CONVEYOR_BELT_3_LEFT,
6215     EL_CONVEYOR_BELT_4_LEFT
6216   };
6217   static int belt_base_active_element[4] =
6218   {
6219     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6220     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6221     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6222     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6223   };
6224 
6225   int x, y, i, j;
6226 
6227   /* set frame order for belt animation graphic according to belt direction */
6228   for (i = 0; i < NUM_BELTS; i++)
6229   {
6230     int belt_nr = i;
6231 
6232     for (j = 0; j < NUM_BELT_PARTS; j++)
6233     {
6234       int element = belt_base_active_element[belt_nr] + j;
6235       int graphic_1 = el2img(element);
6236       int graphic_2 = el2panelimg(element);
6237 
6238       if (game.belt_dir[i] == MV_LEFT)
6239       {
6240 	graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6241 	graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6242       }
6243       else
6244       {
6245 	graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6246 	graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6247       }
6248     }
6249   }
6250 
6251   SCAN_PLAYFIELD(x, y)
6252   {
6253     int element = Feld[x][y];
6254 
6255     for (i = 0; i < NUM_BELTS; i++)
6256     {
6257       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6258       {
6259 	int e_belt_nr = getBeltNrFromBeltElement(element);
6260 	int belt_nr = i;
6261 
6262 	if (e_belt_nr == belt_nr)
6263 	{
6264 	  int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6265 
6266 	  Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6267 	}
6268       }
6269     }
6270   }
6271 }
6272 
ToggleBeltSwitch(int x,int y)6273 static void ToggleBeltSwitch(int x, int y)
6274 {
6275   static int belt_base_element[4] =
6276   {
6277     EL_CONVEYOR_BELT_1_LEFT,
6278     EL_CONVEYOR_BELT_2_LEFT,
6279     EL_CONVEYOR_BELT_3_LEFT,
6280     EL_CONVEYOR_BELT_4_LEFT
6281   };
6282   static int belt_base_active_element[4] =
6283   {
6284     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6285     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6286     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6287     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6288   };
6289   static int belt_base_switch_element[4] =
6290   {
6291     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6292     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6293     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6294     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6295   };
6296   static int belt_move_dir[4] =
6297   {
6298     MV_LEFT,
6299     MV_NONE,
6300     MV_RIGHT,
6301     MV_NONE,
6302   };
6303 
6304   int element = Feld[x][y];
6305   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6306   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6307   int belt_dir = belt_move_dir[belt_dir_nr];
6308   int xx, yy, i;
6309 
6310   if (!IS_BELT_SWITCH(element))
6311     return;
6312 
6313   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6314   game.belt_dir[belt_nr] = belt_dir;
6315 
6316   if (belt_dir_nr == 3)
6317     belt_dir_nr = 1;
6318 
6319   /* set frame order for belt animation graphic according to belt direction */
6320   for (i = 0; i < NUM_BELT_PARTS; i++)
6321   {
6322     int element = belt_base_active_element[belt_nr] + i;
6323     int graphic_1 = el2img(element);
6324     int graphic_2 = el2panelimg(element);
6325 
6326     if (belt_dir == MV_LEFT)
6327     {
6328       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6329       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6330     }
6331     else
6332     {
6333       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6334       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6335     }
6336   }
6337 
6338   SCAN_PLAYFIELD(xx, yy)
6339   {
6340     int element = Feld[xx][yy];
6341 
6342     if (IS_BELT_SWITCH(element))
6343     {
6344       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6345 
6346       if (e_belt_nr == belt_nr)
6347       {
6348 	Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6349 	TEST_DrawLevelField(xx, yy);
6350       }
6351     }
6352     else if (IS_BELT(element) && belt_dir != MV_NONE)
6353     {
6354       int e_belt_nr = getBeltNrFromBeltElement(element);
6355 
6356       if (e_belt_nr == belt_nr)
6357       {
6358 	int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6359 
6360 	Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6361 	TEST_DrawLevelField(xx, yy);
6362       }
6363     }
6364     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6365     {
6366       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6367 
6368       if (e_belt_nr == belt_nr)
6369       {
6370 	int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6371 
6372 	Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6373 	TEST_DrawLevelField(xx, yy);
6374       }
6375     }
6376   }
6377 }
6378 
ToggleSwitchgateSwitch(int x,int y)6379 static void ToggleSwitchgateSwitch(int x, int y)
6380 {
6381   int xx, yy;
6382 
6383   game.switchgate_pos = !game.switchgate_pos;
6384 
6385   SCAN_PLAYFIELD(xx, yy)
6386   {
6387     int element = Feld[xx][yy];
6388 
6389 #if !USE_BOTH_SWITCHGATE_SWITCHES
6390     if (element == EL_SWITCHGATE_SWITCH_UP ||
6391 	element == EL_SWITCHGATE_SWITCH_DOWN)
6392     {
6393       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6394       TEST_DrawLevelField(xx, yy);
6395     }
6396     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6397 	     element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6398     {
6399       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6400       TEST_DrawLevelField(xx, yy);
6401     }
6402 #else
6403     if (element == EL_SWITCHGATE_SWITCH_UP)
6404     {
6405       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6406       TEST_DrawLevelField(xx, yy);
6407     }
6408     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6409     {
6410       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6411       TEST_DrawLevelField(xx, yy);
6412     }
6413     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6414     {
6415       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6416       TEST_DrawLevelField(xx, yy);
6417     }
6418     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6419     {
6420       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6421       TEST_DrawLevelField(xx, yy);
6422     }
6423 #endif
6424     else if (element == EL_SWITCHGATE_OPEN ||
6425 	     element == EL_SWITCHGATE_OPENING)
6426     {
6427       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6428 
6429       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6430     }
6431     else if (element == EL_SWITCHGATE_CLOSED ||
6432 	     element == EL_SWITCHGATE_CLOSING)
6433     {
6434       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6435 
6436       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6437     }
6438   }
6439 }
6440 
getInvisibleActiveFromInvisibleElement(int element)6441 static int getInvisibleActiveFromInvisibleElement(int element)
6442 {
6443   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6444 	  element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6445 	  element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6446 	  element);
6447 }
6448 
getInvisibleFromInvisibleActiveElement(int element)6449 static int getInvisibleFromInvisibleActiveElement(int element)
6450 {
6451   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6452 	  element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6453 	  element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6454 	  element);
6455 }
6456 
RedrawAllLightSwitchesAndInvisibleElements()6457 static void RedrawAllLightSwitchesAndInvisibleElements()
6458 {
6459   int x, y;
6460 
6461   SCAN_PLAYFIELD(x, y)
6462   {
6463     int element = Feld[x][y];
6464 
6465     if (element == EL_LIGHT_SWITCH &&
6466 	game.light_time_left > 0)
6467     {
6468       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6469       TEST_DrawLevelField(x, y);
6470     }
6471     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6472 	     game.light_time_left == 0)
6473     {
6474       Feld[x][y] = EL_LIGHT_SWITCH;
6475       TEST_DrawLevelField(x, y);
6476     }
6477     else if (element == EL_EMC_DRIPPER &&
6478 	     game.light_time_left > 0)
6479     {
6480       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6481       TEST_DrawLevelField(x, y);
6482     }
6483     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6484 	     game.light_time_left == 0)
6485     {
6486       Feld[x][y] = EL_EMC_DRIPPER;
6487       TEST_DrawLevelField(x, y);
6488     }
6489     else if (element == EL_INVISIBLE_STEELWALL ||
6490 	     element == EL_INVISIBLE_WALL ||
6491 	     element == EL_INVISIBLE_SAND)
6492     {
6493       if (game.light_time_left > 0)
6494 	Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6495 
6496       TEST_DrawLevelField(x, y);
6497 
6498       /* uncrumble neighbour fields, if needed */
6499       if (element == EL_INVISIBLE_SAND)
6500 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6501     }
6502     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6503 	     element == EL_INVISIBLE_WALL_ACTIVE ||
6504 	     element == EL_INVISIBLE_SAND_ACTIVE)
6505     {
6506       if (game.light_time_left == 0)
6507 	Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6508 
6509       TEST_DrawLevelField(x, y);
6510 
6511       /* re-crumble neighbour fields, if needed */
6512       if (element == EL_INVISIBLE_SAND)
6513 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6514     }
6515   }
6516 }
6517 
RedrawAllInvisibleElementsForLenses()6518 static void RedrawAllInvisibleElementsForLenses()
6519 {
6520   int x, y;
6521 
6522   SCAN_PLAYFIELD(x, y)
6523   {
6524     int element = Feld[x][y];
6525 
6526     if (element == EL_EMC_DRIPPER &&
6527 	game.lenses_time_left > 0)
6528     {
6529       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6530       TEST_DrawLevelField(x, y);
6531     }
6532     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6533 	     game.lenses_time_left == 0)
6534     {
6535       Feld[x][y] = EL_EMC_DRIPPER;
6536       TEST_DrawLevelField(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL ||
6539 	     element == EL_INVISIBLE_WALL ||
6540 	     element == EL_INVISIBLE_SAND)
6541     {
6542       if (game.lenses_time_left > 0)
6543 	Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6544 
6545       TEST_DrawLevelField(x, y);
6546 
6547       /* uncrumble neighbour fields, if needed */
6548       if (element == EL_INVISIBLE_SAND)
6549 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6552 	     element == EL_INVISIBLE_WALL_ACTIVE ||
6553 	     element == EL_INVISIBLE_SAND_ACTIVE)
6554     {
6555       if (game.lenses_time_left == 0)
6556 	Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6557 
6558       TEST_DrawLevelField(x, y);
6559 
6560       /* re-crumble neighbour fields, if needed */
6561       if (element == EL_INVISIBLE_SAND)
6562 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6563     }
6564   }
6565 }
6566 
RedrawAllInvisibleElementsForMagnifier()6567 static void RedrawAllInvisibleElementsForMagnifier()
6568 {
6569   int x, y;
6570 
6571   SCAN_PLAYFIELD(x, y)
6572   {
6573     int element = Feld[x][y];
6574 
6575     if (element == EL_EMC_FAKE_GRASS &&
6576 	game.magnify_time_left > 0)
6577     {
6578       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6579       TEST_DrawLevelField(x, y);
6580     }
6581     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6582 	     game.magnify_time_left == 0)
6583     {
6584       Feld[x][y] = EL_EMC_FAKE_GRASS;
6585       TEST_DrawLevelField(x, y);
6586     }
6587     else if (IS_GATE_GRAY(element) &&
6588 	     game.magnify_time_left > 0)
6589     {
6590       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6591 		    element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6592 		    IS_EM_GATE_GRAY(element) ?
6593 		    element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6594 		    IS_EMC_GATE_GRAY(element) ?
6595 		    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6596 		    IS_DC_GATE_GRAY(element) ?
6597 		    EL_DC_GATE_WHITE_GRAY_ACTIVE :
6598 		    element);
6599       TEST_DrawLevelField(x, y);
6600     }
6601     else if (IS_GATE_GRAY_ACTIVE(element) &&
6602 	     game.magnify_time_left == 0)
6603     {
6604       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6605 		    element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6606 		    IS_EM_GATE_GRAY_ACTIVE(element) ?
6607 		    element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6608 		    IS_EMC_GATE_GRAY_ACTIVE(element) ?
6609 		    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6610 		    IS_DC_GATE_GRAY_ACTIVE(element) ?
6611 		    EL_DC_GATE_WHITE_GRAY :
6612 		    element);
6613       TEST_DrawLevelField(x, y);
6614     }
6615   }
6616 }
6617 
ToggleLightSwitch(int x,int y)6618 static void ToggleLightSwitch(int x, int y)
6619 {
6620   int element = Feld[x][y];
6621 
6622   game.light_time_left =
6623     (element == EL_LIGHT_SWITCH ?
6624      level.time_light * FRAMES_PER_SECOND : 0);
6625 
6626   RedrawAllLightSwitchesAndInvisibleElements();
6627 }
6628 
ActivateTimegateSwitch(int x,int y)6629 static void ActivateTimegateSwitch(int x, int y)
6630 {
6631   int xx, yy;
6632 
6633   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6634 
6635   SCAN_PLAYFIELD(xx, yy)
6636   {
6637     int element = Feld[xx][yy];
6638 
6639     if (element == EL_TIMEGATE_CLOSED ||
6640 	element == EL_TIMEGATE_CLOSING)
6641     {
6642       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6643       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6644     }
6645 
6646     /*
6647     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6648     {
6649       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6650       TEST_DrawLevelField(xx, yy);
6651     }
6652     */
6653 
6654   }
6655 
6656 #if 1
6657   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6658 		EL_DC_TIMEGATE_SWITCH_ACTIVE);
6659 #else
6660   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6661 #endif
6662 }
6663 
Impact(int x,int y)6664 void Impact(int x, int y)
6665 {
6666   boolean last_line = (y == lev_fieldy - 1);
6667   boolean object_hit = FALSE;
6668   boolean impact = (last_line || object_hit);
6669   int element = Feld[x][y];
6670   int smashed = EL_STEELWALL;
6671 
6672   if (!last_line)	/* check if element below was hit */
6673   {
6674     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6675       return;
6676 
6677     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6678 					 MovDir[x][y + 1] != MV_DOWN ||
6679 					 MovPos[x][y + 1] <= TILEY / 2));
6680 
6681     /* do not smash moving elements that left the smashed field in time */
6682     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6683 	ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6684       object_hit = FALSE;
6685 
6686 #if USE_QUICKSAND_IMPACT_BUGFIX
6687     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6688     {
6689       RemoveMovingField(x, y + 1);
6690       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6691       Feld[x][y + 2] = EL_ROCK;
6692       TEST_DrawLevelField(x, y + 2);
6693 
6694       object_hit = TRUE;
6695     }
6696 
6697     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6698     {
6699       RemoveMovingField(x, y + 1);
6700       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6701       Feld[x][y + 2] = EL_ROCK;
6702       TEST_DrawLevelField(x, y + 2);
6703 
6704       object_hit = TRUE;
6705     }
6706 #endif
6707 
6708     if (object_hit)
6709       smashed = MovingOrBlocked2Element(x, y + 1);
6710 
6711     impact = (last_line || object_hit);
6712   }
6713 
6714   if (!last_line && smashed == EL_ACID)	/* element falls into acid */
6715   {
6716     SplashAcid(x, y + 1);
6717     return;
6718   }
6719 
6720   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6721   /* only reset graphic animation if graphic really changes after impact */
6722   if (impact &&
6723       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6724   {
6725     ResetGfxAnimation(x, y);
6726     TEST_DrawLevelField(x, y);
6727   }
6728 
6729   if (impact && CAN_EXPLODE_IMPACT(element))
6730   {
6731     Bang(x, y);
6732     return;
6733   }
6734   else if (impact && element == EL_PEARL &&
6735 	   smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6736   {
6737     ResetGfxAnimation(x, y);
6738 
6739     Feld[x][y] = EL_PEARL_BREAKING;
6740     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6741     return;
6742   }
6743   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6744   {
6745     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6746 
6747     return;
6748   }
6749 
6750   if (impact && element == EL_AMOEBA_DROP)
6751   {
6752     if (object_hit && IS_PLAYER(x, y + 1))
6753       KillPlayerUnlessEnemyProtected(x, y + 1);
6754     else if (object_hit && smashed == EL_PENGUIN)
6755       Bang(x, y + 1);
6756     else
6757     {
6758       Feld[x][y] = EL_AMOEBA_GROWING;
6759       Store[x][y] = EL_AMOEBA_WET;
6760 
6761       ResetRandomAnimationValue(x, y);
6762     }
6763     return;
6764   }
6765 
6766   if (object_hit)		/* check which object was hit */
6767   {
6768     if ((CAN_PASS_MAGIC_WALL(element) &&
6769 	 (smashed == EL_MAGIC_WALL ||
6770 	  smashed == EL_BD_MAGIC_WALL)) ||
6771 	(CAN_PASS_DC_MAGIC_WALL(element) &&
6772 	 smashed == EL_DC_MAGIC_WALL))
6773     {
6774       int xx, yy;
6775       int activated_magic_wall =
6776 	(smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6777 	 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6778 	 EL_DC_MAGIC_WALL_ACTIVE);
6779 
6780       /* activate magic wall / mill */
6781       SCAN_PLAYFIELD(xx, yy)
6782       {
6783 	if (Feld[xx][yy] == smashed)
6784 	  Feld[xx][yy] = activated_magic_wall;
6785       }
6786 
6787       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6788       game.magic_wall_active = TRUE;
6789 
6790       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6791 			    SND_MAGIC_WALL_ACTIVATING :
6792 			    smashed == EL_BD_MAGIC_WALL ?
6793 			    SND_BD_MAGIC_WALL_ACTIVATING :
6794 			    SND_DC_MAGIC_WALL_ACTIVATING));
6795     }
6796 
6797     if (IS_PLAYER(x, y + 1))
6798     {
6799       if (CAN_SMASH_PLAYER(element))
6800       {
6801 	KillPlayerUnlessEnemyProtected(x, y + 1);
6802 	return;
6803       }
6804     }
6805     else if (smashed == EL_PENGUIN)
6806     {
6807       if (CAN_SMASH_PLAYER(element))
6808       {
6809 	Bang(x, y + 1);
6810 	return;
6811       }
6812     }
6813     else if (element == EL_BD_DIAMOND)
6814     {
6815       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6816       {
6817 	Bang(x, y + 1);
6818 	return;
6819       }
6820     }
6821     else if (((element == EL_SP_INFOTRON ||
6822 	       element == EL_SP_ZONK) &&
6823 	      (smashed == EL_SP_SNIKSNAK ||
6824 	       smashed == EL_SP_ELECTRON ||
6825 	       smashed == EL_SP_DISK_ORANGE)) ||
6826 	     (element == EL_SP_INFOTRON &&
6827 	      smashed == EL_SP_DISK_YELLOW))
6828     {
6829       Bang(x, y + 1);
6830       return;
6831     }
6832     else if (CAN_SMASH_EVERYTHING(element))
6833     {
6834       if (IS_CLASSIC_ENEMY(smashed) ||
6835 	  CAN_EXPLODE_SMASHED(smashed))
6836       {
6837 	Bang(x, y + 1);
6838 	return;
6839       }
6840       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6841       {
6842 	if (smashed == EL_LAMP ||
6843 	    smashed == EL_LAMP_ACTIVE)
6844 	{
6845 	  Bang(x, y + 1);
6846 	  return;
6847 	}
6848 	else if (smashed == EL_NUT)
6849 	{
6850 	  Feld[x][y + 1] = EL_NUT_BREAKING;
6851 	  PlayLevelSound(x, y, SND_NUT_BREAKING);
6852 	  RaiseScoreElement(EL_NUT);
6853 	  return;
6854 	}
6855 	else if (smashed == EL_PEARL)
6856 	{
6857 	  ResetGfxAnimation(x, y);
6858 
6859 	  Feld[x][y + 1] = EL_PEARL_BREAKING;
6860 	  PlayLevelSound(x, y, SND_PEARL_BREAKING);
6861 	  return;
6862 	}
6863 	else if (smashed == EL_DIAMOND)
6864 	{
6865 	  Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6866 	  PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6867 	  return;
6868 	}
6869 	else if (IS_BELT_SWITCH(smashed))
6870 	{
6871 	  ToggleBeltSwitch(x, y + 1);
6872 	}
6873 	else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6874 		 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6875 		 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6876 		 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6877 	{
6878 	  ToggleSwitchgateSwitch(x, y + 1);
6879 	}
6880 	else if (smashed == EL_LIGHT_SWITCH ||
6881 		 smashed == EL_LIGHT_SWITCH_ACTIVE)
6882 	{
6883 	  ToggleLightSwitch(x, y + 1);
6884 	}
6885 	else
6886 	{
6887 #if 0
6888 	  TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6889 #endif
6890 
6891 	  CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6892 
6893 	  CheckElementChangeBySide(x, y + 1, smashed, element,
6894 				   CE_SWITCHED, CH_SIDE_TOP);
6895 	  CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6896 					    CH_SIDE_TOP);
6897 	}
6898       }
6899       else
6900       {
6901 	CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6902       }
6903     }
6904   }
6905 
6906   /* play sound of magic wall / mill */
6907   if (!last_line &&
6908       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6909        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6910        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6911   {
6912     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6913       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6914     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6915       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6916     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6917       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6918 
6919     return;
6920   }
6921 
6922   /* play sound of object that hits the ground */
6923   if (last_line || object_hit)
6924     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6925 }
6926 
TurnRoundExt(int x,int y)6927 inline static void TurnRoundExt(int x, int y)
6928 {
6929   static struct
6930   {
6931     int dx, dy;
6932   } move_xy[] =
6933   {
6934     {  0,  0 },
6935     { -1,  0 },
6936     { +1,  0 },
6937     {  0,  0 },
6938     {  0, -1 },
6939     {  0,  0 }, { 0, 0 }, { 0, 0 },
6940     {  0, +1 }
6941   };
6942   static struct
6943   {
6944     int left, right, back;
6945   } turn[] =
6946   {
6947     { 0,	0,		0	 },
6948     { MV_DOWN,	MV_UP,		MV_RIGHT },
6949     { MV_UP,	MV_DOWN,	MV_LEFT	 },
6950     { 0,	0,		0	 },
6951     { MV_LEFT,	MV_RIGHT,	MV_DOWN	 },
6952     { 0,	0,		0	 },
6953     { 0,	0,		0	 },
6954     { 0,	0,		0	 },
6955     { MV_RIGHT,	MV_LEFT,	MV_UP	 }
6956   };
6957 
6958   int element = Feld[x][y];
6959   int move_pattern = element_info[element].move_pattern;
6960 
6961   int old_move_dir = MovDir[x][y];
6962   int left_dir  = turn[old_move_dir].left;
6963   int right_dir = turn[old_move_dir].right;
6964   int back_dir  = turn[old_move_dir].back;
6965 
6966   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6967   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6968   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6969   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6970 
6971   int left_x  = x + left_dx,  left_y  = y + left_dy;
6972   int right_x = x + right_dx, right_y = y + right_dy;
6973   int move_x  = x + move_dx,  move_y  = y + move_dy;
6974 
6975   int xx, yy;
6976 
6977   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6978   {
6979     TestIfBadThingTouchesOtherBadThing(x, y);
6980 
6981     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6982       MovDir[x][y] = right_dir;
6983     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6984       MovDir[x][y] = left_dir;
6985 
6986     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6987       MovDelay[x][y] = 9;
6988     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6989       MovDelay[x][y] = 1;
6990   }
6991   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6992   {
6993     TestIfBadThingTouchesOtherBadThing(x, y);
6994 
6995     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6996       MovDir[x][y] = left_dir;
6997     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6998       MovDir[x][y] = right_dir;
6999 
7000     if (element == EL_SPACESHIP	&& MovDir[x][y] != old_move_dir)
7001       MovDelay[x][y] = 9;
7002     else if (element == EL_BD_FIREFLY)	    /* && MovDir[x][y] == right_dir) */
7003       MovDelay[x][y] = 1;
7004   }
7005   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008 
7009     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7010       MovDir[x][y] = left_dir;
7011     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7012       MovDir[x][y] = right_dir;
7013 
7014     if (MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016   }
7017   else if (element == EL_YAMYAM)
7018   {
7019     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7020     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7021 
7022     if (can_turn_left && can_turn_right)
7023       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7024     else if (can_turn_left)
7025       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7026     else if (can_turn_right)
7027       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7028     else
7029       MovDir[x][y] = back_dir;
7030 
7031     MovDelay[x][y] = 16 + 16 * RND(3);
7032   }
7033   else if (element == EL_DARK_YAMYAM)
7034   {
7035     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7036 							 left_x, left_y);
7037     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7038 							 right_x, right_y);
7039 
7040     if (can_turn_left && can_turn_right)
7041       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7042     else if (can_turn_left)
7043       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7044     else if (can_turn_right)
7045       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7046     else
7047       MovDir[x][y] = back_dir;
7048 
7049     MovDelay[x][y] = 16 + 16 * RND(3);
7050   }
7051   else if (element == EL_PACMAN)
7052   {
7053     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7054     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7055 
7056     if (can_turn_left && can_turn_right)
7057       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7058     else if (can_turn_left)
7059       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7060     else if (can_turn_right)
7061       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7062     else
7063       MovDir[x][y] = back_dir;
7064 
7065     MovDelay[x][y] = 6 + RND(40);
7066   }
7067   else if (element == EL_PIG)
7068   {
7069     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7070     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7071     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7072     boolean should_turn_left, should_turn_right, should_move_on;
7073     int rnd_value = 24;
7074     int rnd = RND(rnd_value);
7075 
7076     should_turn_left = (can_turn_left &&
7077 			(!can_move_on ||
7078 			 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7079 						   y + back_dy + left_dy)));
7080     should_turn_right = (can_turn_right &&
7081 			 (!can_move_on ||
7082 			  IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7083 						    y + back_dy + right_dy)));
7084     should_move_on = (can_move_on &&
7085 		      (!can_turn_left ||
7086 		       !can_turn_right ||
7087 		       IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7088 						 y + move_dy + left_dy) ||
7089 		       IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7090 						 y + move_dy + right_dy)));
7091 
7092     if (should_turn_left || should_turn_right || should_move_on)
7093     {
7094       if (should_turn_left && should_turn_right && should_move_on)
7095 	MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7096 			rnd < 2 * rnd_value / 3 ? right_dir :
7097 			old_move_dir);
7098       else if (should_turn_left && should_turn_right)
7099 	MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7100       else if (should_turn_left && should_move_on)
7101 	MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7102       else if (should_turn_right && should_move_on)
7103 	MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7104       else if (should_turn_left)
7105 	MovDir[x][y] = left_dir;
7106       else if (should_turn_right)
7107 	MovDir[x][y] = right_dir;
7108       else if (should_move_on)
7109 	MovDir[x][y] = old_move_dir;
7110     }
7111     else if (can_move_on && rnd > rnd_value / 8)
7112       MovDir[x][y] = old_move_dir;
7113     else if (can_turn_left && can_turn_right)
7114       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7115     else if (can_turn_left && rnd > rnd_value / 8)
7116       MovDir[x][y] = left_dir;
7117     else if (can_turn_right && rnd > rnd_value/8)
7118       MovDir[x][y] = right_dir;
7119     else
7120       MovDir[x][y] = back_dir;
7121 
7122     xx = x + move_xy[MovDir[x][y]].dx;
7123     yy = y + move_xy[MovDir[x][y]].dy;
7124 
7125     if (!IN_LEV_FIELD(xx, yy) ||
7126         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7127       MovDir[x][y] = old_move_dir;
7128 
7129     MovDelay[x][y] = 0;
7130   }
7131   else if (element == EL_DRAGON)
7132   {
7133     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7134     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7135     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7136     int rnd_value = 24;
7137     int rnd = RND(rnd_value);
7138 
7139     if (can_move_on && rnd > rnd_value / 8)
7140       MovDir[x][y] = old_move_dir;
7141     else if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143     else if (can_turn_left && rnd > rnd_value / 8)
7144       MovDir[x][y] = left_dir;
7145     else if (can_turn_right && rnd > rnd_value / 8)
7146       MovDir[x][y] = right_dir;
7147     else
7148       MovDir[x][y] = back_dir;
7149 
7150     xx = x + move_xy[MovDir[x][y]].dx;
7151     yy = y + move_xy[MovDir[x][y]].dy;
7152 
7153     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7154       MovDir[x][y] = old_move_dir;
7155 
7156     MovDelay[x][y] = 0;
7157   }
7158   else if (element == EL_MOLE)
7159   {
7160     boolean can_move_on =
7161       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7162 			    IS_AMOEBOID(Feld[move_x][move_y]) ||
7163 			    Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7164     if (!can_move_on)
7165     {
7166       boolean can_turn_left =
7167 	(MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7168 			      IS_AMOEBOID(Feld[left_x][left_y])));
7169 
7170       boolean can_turn_right =
7171 	(MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7172 			      IS_AMOEBOID(Feld[right_x][right_y])));
7173 
7174       if (can_turn_left && can_turn_right)
7175 	MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7176       else if (can_turn_left)
7177 	MovDir[x][y] = left_dir;
7178       else
7179 	MovDir[x][y] = right_dir;
7180     }
7181 
7182     if (MovDir[x][y] != old_move_dir)
7183       MovDelay[x][y] = 9;
7184   }
7185   else if (element == EL_BALLOON)
7186   {
7187     MovDir[x][y] = game.wind_direction;
7188     MovDelay[x][y] = 0;
7189   }
7190   else if (element == EL_SPRING)
7191   {
7192 #if USE_NEW_SPRING_BUMPER
7193     if (MovDir[x][y] & MV_HORIZONTAL)
7194     {
7195       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7196 	  !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7197       {
7198 	Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7199 	ResetGfxAnimation(move_x, move_y);
7200 	TEST_DrawLevelField(move_x, move_y);
7201 
7202 	MovDir[x][y] = back_dir;
7203       }
7204       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7205 	       SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7206 	MovDir[x][y] = MV_NONE;
7207     }
7208 #else
7209     if (MovDir[x][y] & MV_HORIZONTAL &&
7210 	(!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7211 	 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7212       MovDir[x][y] = MV_NONE;
7213 #endif
7214 
7215     MovDelay[x][y] = 0;
7216   }
7217   else if (element == EL_ROBOT ||
7218 	   element == EL_SATELLITE ||
7219 	   element == EL_PENGUIN ||
7220 	   element == EL_EMC_ANDROID)
7221   {
7222     int attr_x = -1, attr_y = -1;
7223 
7224     if (AllPlayersGone)
7225     {
7226       attr_x = ExitX;
7227       attr_y = ExitY;
7228     }
7229     else
7230     {
7231       int i;
7232 
7233       for (i = 0; i < MAX_PLAYERS; i++)
7234       {
7235 	struct PlayerInfo *player = &stored_player[i];
7236 	int jx = player->jx, jy = player->jy;
7237 
7238 	if (!player->active)
7239 	  continue;
7240 
7241 	if (attr_x == -1 ||
7242 	    ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7243 	{
7244 	  attr_x = jx;
7245 	  attr_y = jy;
7246 	}
7247       }
7248     }
7249 
7250     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7251 	(Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7252 	 game.engine_version < VERSION_IDENT(3,1,0,0)))
7253     {
7254       attr_x = ZX;
7255       attr_y = ZY;
7256     }
7257 
7258     if (element == EL_PENGUIN)
7259     {
7260       int i;
7261       static int xy[4][2] =
7262       {
7263 	{ 0, -1 },
7264 	{ -1, 0 },
7265 	{ +1, 0 },
7266 	{ 0, +1 }
7267       };
7268 
7269       for (i = 0; i < NUM_DIRECTIONS; i++)
7270       {
7271     	int ex = x + xy[i][0];
7272     	int ey = y + xy[i][1];
7273 
7274     	if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7275 				     Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7276 				     Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7277 				     Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7278 	{
7279 	  attr_x = ex;
7280  	  attr_y = ey;
7281 	  break;
7282 	}
7283       }
7284     }
7285 
7286     MovDir[x][y] = MV_NONE;
7287     if (attr_x < x)
7288       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7289     else if (attr_x > x)
7290       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7291     if (attr_y < y)
7292       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7293     else if (attr_y > y)
7294       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7295 
7296     if (element == EL_ROBOT)
7297     {
7298       int newx, newy;
7299 
7300       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7301 	MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7302       Moving2Blocked(x, y, &newx, &newy);
7303 
7304       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7305 	MovDelay[x][y] = 8 + 8 * !RND(3);
7306       else
7307 	MovDelay[x][y] = 16;
7308     }
7309     else if (element == EL_PENGUIN)
7310     {
7311       int newx, newy;
7312 
7313       MovDelay[x][y] = 1;
7314 
7315       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7316       {
7317 	boolean first_horiz = RND(2);
7318 	int new_move_dir = MovDir[x][y];
7319 
7320 	MovDir[x][y] =
7321 	  new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7322 	Moving2Blocked(x, y, &newx, &newy);
7323 
7324 	if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7325 	  return;
7326 
7327 	MovDir[x][y] =
7328 	  new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7329 	Moving2Blocked(x, y, &newx, &newy);
7330 
7331 	if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7332 	  return;
7333 
7334 	MovDir[x][y] = old_move_dir;
7335 	return;
7336       }
7337     }
7338     else if (element == EL_SATELLITE)
7339     {
7340       int newx, newy;
7341 
7342       MovDelay[x][y] = 1;
7343 
7344       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7345       {
7346 	boolean first_horiz = RND(2);
7347 	int new_move_dir = MovDir[x][y];
7348 
7349 	MovDir[x][y] =
7350 	  new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7351 	Moving2Blocked(x, y, &newx, &newy);
7352 
7353 	if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7354 	  return;
7355 
7356 	MovDir[x][y] =
7357 	  new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7358 	Moving2Blocked(x, y, &newx, &newy);
7359 
7360 	if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7361 	  return;
7362 
7363 	MovDir[x][y] = old_move_dir;
7364 	return;
7365       }
7366     }
7367     else if (element == EL_EMC_ANDROID)
7368     {
7369       static int check_pos[16] =
7370       {
7371 	-1,		/*  0 => (invalid)          */
7372 	7,		/*  1 => MV_LEFT            */
7373 	3,		/*  2 => MV_RIGHT           */
7374 	-1,		/*  3 => (invalid)          */
7375 	1,		/*  4 =>            MV_UP   */
7376 	0,		/*  5 => MV_LEFT  | MV_UP   */
7377 	2,		/*  6 => MV_RIGHT | MV_UP   */
7378 	-1,		/*  7 => (invalid)          */
7379 	5,		/*  8 =>            MV_DOWN */
7380 	6,		/*  9 => MV_LEFT  | MV_DOWN */
7381 	4,		/* 10 => MV_RIGHT | MV_DOWN */
7382 	-1,		/* 11 => (invalid)          */
7383 	-1,		/* 12 => (invalid)          */
7384 	-1,		/* 13 => (invalid)          */
7385 	-1,		/* 14 => (invalid)          */
7386 	-1,		/* 15 => (invalid)          */
7387       };
7388       static struct
7389       {
7390 	int dx, dy;
7391 	int dir;
7392       } check_xy[8] =
7393       {
7394         { -1, -1,	MV_LEFT  | MV_UP   },
7395        	{  0, -1,	           MV_UP   },
7396 	{ +1, -1,	MV_RIGHT | MV_UP   },
7397 	{ +1,  0,	MV_RIGHT           },
7398 	{ +1, +1,	MV_RIGHT | MV_DOWN },
7399 	{  0, +1,	           MV_DOWN },
7400 	{ -1, +1,	MV_LEFT  | MV_DOWN },
7401 	{ -1,  0,	MV_LEFT            },
7402       };
7403       int start_pos, check_order;
7404       boolean can_clone = FALSE;
7405       int i;
7406 
7407       /* check if there is any free field around current position */
7408       for (i = 0; i < 8; i++)
7409       {
7410 	int newx = x + check_xy[i].dx;
7411 	int newy = y + check_xy[i].dy;
7412 
7413 	if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7414 	{
7415 	  can_clone = TRUE;
7416 
7417 	  break;
7418 	}
7419       }
7420 
7421       if (can_clone)		/* randomly find an element to clone */
7422       {
7423 	can_clone = FALSE;
7424 
7425 	start_pos = check_pos[RND(8)];
7426 	check_order = (RND(2) ? -1 : +1);
7427 
7428 	for (i = 0; i < 8; i++)
7429 	{
7430 	  int pos_raw = start_pos + i * check_order;
7431 	  int pos = (pos_raw + 8) % 8;
7432 	  int newx = x + check_xy[pos].dx;
7433 	  int newy = y + check_xy[pos].dy;
7434 
7435 	  if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7436 	  {
7437 	    element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7438 	    element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7439 
7440 	    Store[x][y] = Feld[newx][newy];
7441 
7442 	    can_clone = TRUE;
7443 
7444 	    break;
7445 	  }
7446 	}
7447       }
7448 
7449       if (can_clone)		/* randomly find a direction to move */
7450       {
7451 	can_clone = FALSE;
7452 
7453 	start_pos = check_pos[RND(8)];
7454 	check_order = (RND(2) ? -1 : +1);
7455 
7456 	for (i = 0; i < 8; i++)
7457 	{
7458 	  int pos_raw = start_pos + i * check_order;
7459 	  int pos = (pos_raw + 8) % 8;
7460 	  int newx = x + check_xy[pos].dx;
7461 	  int newy = y + check_xy[pos].dy;
7462 	  int new_move_dir = check_xy[pos].dir;
7463 
7464 	  if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7465 	  {
7466 	    MovDir[x][y] = new_move_dir;
7467 	    MovDelay[x][y] = level.android_clone_time * 8 + 1;
7468 
7469 	    can_clone = TRUE;
7470 
7471 	    break;
7472 	  }
7473 	}
7474       }
7475 
7476       if (can_clone)		/* cloning and moving successful */
7477 	return;
7478 
7479       /* cannot clone -- try to move towards player */
7480 
7481       start_pos = check_pos[MovDir[x][y] & 0x0f];
7482       check_order = (RND(2) ? -1 : +1);
7483 
7484       for (i = 0; i < 3; i++)
7485       {
7486 	/* first check start_pos, then previous/next or (next/previous) pos */
7487 	int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7488 	int pos = (pos_raw + 8) % 8;
7489 	int newx = x + check_xy[pos].dx;
7490 	int newy = y + check_xy[pos].dy;
7491 	int new_move_dir = check_xy[pos].dir;
7492 
7493 	if (IS_PLAYER(newx, newy))
7494 	  break;
7495 
7496 	if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7497 	{
7498 	  MovDir[x][y] = new_move_dir;
7499 	  MovDelay[x][y] = level.android_move_time * 8 + 1;
7500 
7501 	  break;
7502 	}
7503       }
7504     }
7505   }
7506   else if (move_pattern == MV_TURNING_LEFT ||
7507 	   move_pattern == MV_TURNING_RIGHT ||
7508 	   move_pattern == MV_TURNING_LEFT_RIGHT ||
7509 	   move_pattern == MV_TURNING_RIGHT_LEFT ||
7510 	   move_pattern == MV_TURNING_RANDOM ||
7511 	   move_pattern == MV_ALL_DIRECTIONS)
7512   {
7513     boolean can_turn_left =
7514       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7515     boolean can_turn_right =
7516       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7517 
7518     if (element_info[element].move_stepsize == 0)	/* "not moving" */
7519       return;
7520 
7521     if (move_pattern == MV_TURNING_LEFT)
7522       MovDir[x][y] = left_dir;
7523     else if (move_pattern == MV_TURNING_RIGHT)
7524       MovDir[x][y] = right_dir;
7525     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7526       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7527     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7528       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7529     else if (move_pattern == MV_TURNING_RANDOM)
7530       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7531 		      can_turn_right && !can_turn_left ? right_dir :
7532 		      RND(2) ? left_dir : right_dir);
7533     else if (can_turn_left && can_turn_right)
7534       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7535     else if (can_turn_left)
7536       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7537     else if (can_turn_right)
7538       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7539     else
7540       MovDir[x][y] = back_dir;
7541 
7542     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7543   }
7544   else if (move_pattern == MV_HORIZONTAL ||
7545 	   move_pattern == MV_VERTICAL)
7546   {
7547     if (move_pattern & old_move_dir)
7548       MovDir[x][y] = back_dir;
7549     else if (move_pattern == MV_HORIZONTAL)
7550       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7551     else if (move_pattern == MV_VERTICAL)
7552       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7553 
7554     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7555   }
7556   else if (move_pattern & MV_ANY_DIRECTION)
7557   {
7558     MovDir[x][y] = move_pattern;
7559     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern & MV_WIND_DIRECTION)
7562   {
7563     MovDir[x][y] = game.wind_direction;
7564     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7565   }
7566   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7567   {
7568     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7569       MovDir[x][y] = left_dir;
7570     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7571       MovDir[x][y] = right_dir;
7572 
7573     if (MovDir[x][y] != old_move_dir)
7574       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7575   }
7576   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7577   {
7578     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7579       MovDir[x][y] = right_dir;
7580     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7581       MovDir[x][y] = left_dir;
7582 
7583     if (MovDir[x][y] != old_move_dir)
7584       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7585   }
7586   else if (move_pattern == MV_TOWARDS_PLAYER ||
7587 	   move_pattern == MV_AWAY_FROM_PLAYER)
7588   {
7589     int attr_x = -1, attr_y = -1;
7590     int newx, newy;
7591     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7592 
7593     if (AllPlayersGone)
7594     {
7595       attr_x = ExitX;
7596       attr_y = ExitY;
7597     }
7598     else
7599     {
7600       int i;
7601 
7602       for (i = 0; i < MAX_PLAYERS; i++)
7603       {
7604 	struct PlayerInfo *player = &stored_player[i];
7605 	int jx = player->jx, jy = player->jy;
7606 
7607 	if (!player->active)
7608 	  continue;
7609 
7610 	if (attr_x == -1 ||
7611 	    ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7612 	{
7613 	  attr_x = jx;
7614 	  attr_y = jy;
7615 	}
7616       }
7617     }
7618 
7619     MovDir[x][y] = MV_NONE;
7620     if (attr_x < x)
7621       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7622     else if (attr_x > x)
7623       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7624     if (attr_y < y)
7625       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7626     else if (attr_y > y)
7627       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7628 
7629     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7630 
7631     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7632     {
7633       boolean first_horiz = RND(2);
7634       int new_move_dir = MovDir[x][y];
7635 
7636       if (element_info[element].move_stepsize == 0)	/* "not moving" */
7637       {
7638 	first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7639 	MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7640 
7641 	return;
7642       }
7643 
7644       MovDir[x][y] =
7645 	new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7646       Moving2Blocked(x, y, &newx, &newy);
7647 
7648       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7649 	return;
7650 
7651       MovDir[x][y] =
7652 	new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7653       Moving2Blocked(x, y, &newx, &newy);
7654 
7655       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7656 	return;
7657 
7658       MovDir[x][y] = old_move_dir;
7659     }
7660   }
7661   else if (move_pattern == MV_WHEN_PUSHED ||
7662 	   move_pattern == MV_WHEN_DROPPED)
7663   {
7664     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7665       MovDir[x][y] = MV_NONE;
7666 
7667     MovDelay[x][y] = 0;
7668   }
7669   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7670   {
7671     static int test_xy[7][2] =
7672     {
7673       { 0, -1 },
7674       { -1, 0 },
7675       { +1, 0 },
7676       { 0, +1 },
7677       { 0, -1 },
7678       { -1, 0 },
7679       { +1, 0 },
7680     };
7681     static int test_dir[7] =
7682     {
7683       MV_UP,
7684       MV_LEFT,
7685       MV_RIGHT,
7686       MV_DOWN,
7687       MV_UP,
7688       MV_LEFT,
7689       MV_RIGHT,
7690     };
7691     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7692     int move_preference = -1000000;	/* start with very low preference */
7693     int new_move_dir = MV_NONE;
7694     int start_test = RND(4);
7695     int i;
7696 
7697     for (i = 0; i < NUM_DIRECTIONS; i++)
7698     {
7699       int move_dir = test_dir[start_test + i];
7700       int move_dir_preference;
7701 
7702       xx = x + test_xy[start_test + i][0];
7703       yy = y + test_xy[start_test + i][1];
7704 
7705       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706 	  (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7707       {
7708 	new_move_dir = move_dir;
7709 
7710 	break;
7711       }
7712 
7713       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7714 	continue;
7715 
7716       move_dir_preference = -1 * RunnerVisit[xx][yy];
7717       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718 	move_dir_preference = PlayerVisit[xx][yy];
7719 
7720       if (move_dir_preference > move_preference)
7721       {
7722 	/* prefer field that has not been visited for the longest time */
7723 	move_preference = move_dir_preference;
7724 	new_move_dir = move_dir;
7725       }
7726       else if (move_dir_preference == move_preference &&
7727 	       move_dir == old_move_dir)
7728       {
7729 	/* prefer last direction when all directions are preferred equally */
7730 	move_preference = move_dir_preference;
7731 	new_move_dir = move_dir;
7732       }
7733     }
7734 
7735     MovDir[x][y] = new_move_dir;
7736     if (old_move_dir != new_move_dir)
7737       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738   }
7739 }
7740 
TurnRound(int x,int y)7741 static void TurnRound(int x, int y)
7742 {
7743   int direction = MovDir[x][y];
7744 
7745   TurnRoundExt(x, y);
7746 
7747   GfxDir[x][y] = MovDir[x][y];
7748 
7749   if (direction != MovDir[x][y])
7750     GfxFrame[x][y] = 0;
7751 
7752   if (MovDelay[x][y])
7753     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7754 
7755   ResetGfxFrame(x, y, FALSE);
7756 }
7757 
JustBeingPushed(int x,int y)7758 static boolean JustBeingPushed(int x, int y)
7759 {
7760   int i;
7761 
7762   for (i = 0; i < MAX_PLAYERS; i++)
7763   {
7764     struct PlayerInfo *player = &stored_player[i];
7765 
7766     if (player->active && player->is_pushing && player->MovPos)
7767     {
7768       int next_jx = player->jx + (player->jx - player->last_jx);
7769       int next_jy = player->jy + (player->jy - player->last_jy);
7770 
7771       if (x == next_jx && y == next_jy)
7772 	return TRUE;
7773     }
7774   }
7775 
7776   return FALSE;
7777 }
7778 
StartMoving(int x,int y)7779 void StartMoving(int x, int y)
7780 {
7781   boolean started_moving = FALSE;	/* some elements can fall _and_ move */
7782   int element = Feld[x][y];
7783 
7784   if (Stop[x][y])
7785     return;
7786 
7787   if (MovDelay[x][y] == 0)
7788     GfxAction[x][y] = ACTION_DEFAULT;
7789 
7790   if (CAN_FALL(element) && y < lev_fieldy - 1)
7791   {
7792     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7793 	(x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794       if (JustBeingPushed(x, y))
7795 	return;
7796 
7797     if (element == EL_QUICKSAND_FULL)
7798     {
7799       if (IS_FREE(x, y + 1))
7800       {
7801 	InitMovingField(x, y, MV_DOWN);
7802 	started_moving = TRUE;
7803 
7804 	Feld[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806 	if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807 	  Store[x][y] = EL_ROCK;
7808 #else
7809 	Store[x][y] = EL_ROCK;
7810 #endif
7811 
7812 	PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7813       }
7814       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7815       {
7816 	if (!MovDelay[x][y])
7817 	{
7818 	  MovDelay[x][y] = TILEY + 1;
7819 
7820 	  ResetGfxAnimation(x, y);
7821 	  ResetGfxAnimation(x, y + 1);
7822 	}
7823 
7824 	if (MovDelay[x][y])
7825 	{
7826 	  DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7828 
7829 	  MovDelay[x][y]--;
7830 	  if (MovDelay[x][y])
7831 	    return;
7832 	}
7833 
7834 	Feld[x][y] = EL_QUICKSAND_EMPTY;
7835 	Feld[x][y + 1] = EL_QUICKSAND_FULL;
7836 	Store[x][y + 1] = Store[x][y];
7837 	Store[x][y] = 0;
7838 
7839 	PlayLevelSoundAction(x, y, ACTION_FILLING);
7840       }
7841       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7842       {
7843 	if (!MovDelay[x][y])
7844 	{
7845 	  MovDelay[x][y] = TILEY + 1;
7846 
7847 	  ResetGfxAnimation(x, y);
7848 	  ResetGfxAnimation(x, y + 1);
7849 	}
7850 
7851 	if (MovDelay[x][y])
7852 	{
7853 	  DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7855 
7856 	  MovDelay[x][y]--;
7857 	  if (MovDelay[x][y])
7858 	    return;
7859 	}
7860 
7861 	Feld[x][y] = EL_QUICKSAND_EMPTY;
7862 	Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863 	Store[x][y + 1] = Store[x][y];
7864 	Store[x][y] = 0;
7865 
7866 	PlayLevelSoundAction(x, y, ACTION_FILLING);
7867       }
7868     }
7869     else if (element == EL_QUICKSAND_FAST_FULL)
7870     {
7871       if (IS_FREE(x, y + 1))
7872       {
7873 	InitMovingField(x, y, MV_DOWN);
7874 	started_moving = TRUE;
7875 
7876 	Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878 	if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879 	  Store[x][y] = EL_ROCK;
7880 #else
7881 	Store[x][y] = EL_ROCK;
7882 #endif
7883 
7884 	PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7885       }
7886       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887       {
7888 	if (!MovDelay[x][y])
7889 	{
7890 	  MovDelay[x][y] = TILEY + 1;
7891 
7892 	  ResetGfxAnimation(x, y);
7893 	  ResetGfxAnimation(x, y + 1);
7894 	}
7895 
7896 	if (MovDelay[x][y])
7897 	{
7898 	  DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7900 
7901 	  MovDelay[x][y]--;
7902 	  if (MovDelay[x][y])
7903 	    return;
7904 	}
7905 
7906 	Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907 	Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908 	Store[x][y + 1] = Store[x][y];
7909 	Store[x][y] = 0;
7910 
7911 	PlayLevelSoundAction(x, y, ACTION_FILLING);
7912       }
7913       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7914       {
7915 	if (!MovDelay[x][y])
7916 	{
7917 	  MovDelay[x][y] = TILEY + 1;
7918 
7919 	  ResetGfxAnimation(x, y);
7920 	  ResetGfxAnimation(x, y + 1);
7921 	}
7922 
7923 	if (MovDelay[x][y])
7924 	{
7925 	  DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7927 
7928 	  MovDelay[x][y]--;
7929 	  if (MovDelay[x][y])
7930 	    return;
7931 	}
7932 
7933 	Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934 	Feld[x][y + 1] = EL_QUICKSAND_FULL;
7935 	Store[x][y + 1] = Store[x][y];
7936 	Store[x][y] = 0;
7937 
7938 	PlayLevelSoundAction(x, y, ACTION_FILLING);
7939       }
7940     }
7941     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942 	     Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7943     {
7944       InitMovingField(x, y, MV_DOWN);
7945       started_moving = TRUE;
7946 
7947       Feld[x][y] = EL_QUICKSAND_FILLING;
7948       Store[x][y] = element;
7949 
7950       PlayLevelSoundAction(x, y, ACTION_FILLING);
7951     }
7952     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953 	     Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7954     {
7955       InitMovingField(x, y, MV_DOWN);
7956       started_moving = TRUE;
7957 
7958       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7959       Store[x][y] = element;
7960 
7961       PlayLevelSoundAction(x, y, ACTION_FILLING);
7962     }
7963     else if (element == EL_MAGIC_WALL_FULL)
7964     {
7965       if (IS_FREE(x, y + 1))
7966       {
7967 	InitMovingField(x, y, MV_DOWN);
7968 	started_moving = TRUE;
7969 
7970 	Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7971 	Store[x][y] = EL_CHANGED(Store[x][y]);
7972       }
7973       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7974       {
7975 	if (!MovDelay[x][y])
7976 	  MovDelay[x][y] = TILEY / 4 + 1;
7977 
7978 	if (MovDelay[x][y])
7979 	{
7980 	  MovDelay[x][y]--;
7981 	  if (MovDelay[x][y])
7982 	    return;
7983 	}
7984 
7985 	Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7986 	Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7987 	Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7988 	Store[x][y] = 0;
7989       }
7990     }
7991     else if (element == EL_BD_MAGIC_WALL_FULL)
7992     {
7993       if (IS_FREE(x, y + 1))
7994       {
7995 	InitMovingField(x, y, MV_DOWN);
7996 	started_moving = TRUE;
7997 
7998 	Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999 	Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8000       }
8001       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8002       {
8003 	if (!MovDelay[x][y])
8004 	  MovDelay[x][y] = TILEY / 4 + 1;
8005 
8006 	if (MovDelay[x][y])
8007 	{
8008 	  MovDelay[x][y]--;
8009 	  if (MovDelay[x][y])
8010 	    return;
8011 	}
8012 
8013 	Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014 	Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015 	Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8016 	Store[x][y] = 0;
8017       }
8018     }
8019     else if (element == EL_DC_MAGIC_WALL_FULL)
8020     {
8021       if (IS_FREE(x, y + 1))
8022       {
8023 	InitMovingField(x, y, MV_DOWN);
8024 	started_moving = TRUE;
8025 
8026 	Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027 	Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8028       }
8029       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8030       {
8031 	if (!MovDelay[x][y])
8032 	  MovDelay[x][y] = TILEY / 4 + 1;
8033 
8034 	if (MovDelay[x][y])
8035 	{
8036 	  MovDelay[x][y]--;
8037 	  if (MovDelay[x][y])
8038 	    return;
8039 	}
8040 
8041 	Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042 	Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043 	Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8044 	Store[x][y] = 0;
8045       }
8046     }
8047     else if ((CAN_PASS_MAGIC_WALL(element) &&
8048 	      (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049 	       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050 	     (CAN_PASS_DC_MAGIC_WALL(element) &&
8051 	      (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8052 
8053     {
8054       InitMovingField(x, y, MV_DOWN);
8055       started_moving = TRUE;
8056 
8057       Feld[x][y] =
8058 	(Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059 	 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060 	 EL_DC_MAGIC_WALL_FILLING);
8061       Store[x][y] = element;
8062     }
8063     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8064     {
8065       SplashAcid(x, y + 1);
8066 
8067       InitMovingField(x, y, MV_DOWN);
8068       started_moving = TRUE;
8069 
8070       Store[x][y] = EL_ACID;
8071     }
8072     else if (
8073 #if USE_FIX_IMPACT_COLLISION
8074 	     (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8075 	      CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8076 #else
8077 	     (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8078 	      CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8079 #endif
8080 	     (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8081 	      CAN_FALL(element) && WasJustFalling[x][y] &&
8082 	      (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8083 
8084 	     (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8085 	      CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8086 	      (Feld[x][y + 1] == EL_BLOCKED)))
8087     {
8088       /* this is needed for a special case not covered by calling "Impact()"
8089 	 from "ContinueMoving()": if an element moves to a tile directly below
8090 	 another element which was just falling on that tile (which was empty
8091 	 in the previous frame), the falling element above would just stop
8092 	 instead of smashing the element below (in previous version, the above
8093 	 element was just checked for "moving" instead of "falling", resulting
8094 	 in incorrect smashes caused by horizontal movement of the above
8095 	 element; also, the case of the player being the element to smash was
8096 	 simply not covered here... :-/ ) */
8097 
8098       CheckCollision[x][y] = 0;
8099       CheckImpact[x][y] = 0;
8100 
8101       Impact(x, y);
8102     }
8103     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8104     {
8105       if (MovDir[x][y] == MV_NONE)
8106       {
8107 	InitMovingField(x, y, MV_DOWN);
8108 	started_moving = TRUE;
8109       }
8110     }
8111     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8112     {
8113       if (WasJustFalling[x][y])	/* prevent animation from being restarted */
8114 	MovDir[x][y] = MV_DOWN;
8115 
8116       InitMovingField(x, y, MV_DOWN);
8117       started_moving = TRUE;
8118     }
8119     else if (element == EL_AMOEBA_DROP)
8120     {
8121       Feld[x][y] = EL_AMOEBA_GROWING;
8122       Store[x][y] = EL_AMOEBA_WET;
8123     }
8124     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8125 	      (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8126 	     !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8127 	     element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8128     {
8129       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8130 				(IS_FREE(x - 1, y + 1) ||
8131 				 Feld[x - 1][y + 1] == EL_ACID));
8132       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8133 				(IS_FREE(x + 1, y + 1) ||
8134 				 Feld[x + 1][y + 1] == EL_ACID));
8135       boolean can_fall_any  = (can_fall_left || can_fall_right);
8136       boolean can_fall_both = (can_fall_left && can_fall_right);
8137       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8138 
8139 #if USE_NEW_ALL_SLIPPERY
8140       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8141       {
8142 	if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143 	  can_fall_right = FALSE;
8144 	else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145 	  can_fall_left = FALSE;
8146 	else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147 	  can_fall_right = FALSE;
8148 	else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149 	  can_fall_left = FALSE;
8150 
8151 	can_fall_any  = (can_fall_left || can_fall_right);
8152 	can_fall_both = FALSE;
8153       }
8154 #else
8155       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8156       {
8157 	if (slippery_type == SLIPPERY_ONLY_LEFT)
8158 	  can_fall_right = FALSE;
8159 	else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8160 	  can_fall_left = FALSE;
8161 	else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8162 	  can_fall_right = FALSE;
8163 	else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8164 	  can_fall_left = FALSE;
8165 
8166 	can_fall_any  = (can_fall_left || can_fall_right);
8167 	can_fall_both = (can_fall_left && can_fall_right);
8168       }
8169 #endif
8170 
8171 #if USE_NEW_ALL_SLIPPERY
8172 #else
8173 #if USE_NEW_SP_SLIPPERY
8174       /* !!! better use the same properties as for custom elements here !!! */
8175       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8176 	       can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8177       {
8178 	can_fall_right = FALSE;		/* slip down on left side */
8179 	can_fall_both = FALSE;
8180       }
8181 #endif
8182 #endif
8183 
8184 #if USE_NEW_ALL_SLIPPERY
8185       if (can_fall_both)
8186       {
8187 	if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8188 	  can_fall_right = FALSE;	/* slip down on left side */
8189 	else
8190 	  can_fall_left = !(can_fall_right = RND(2));
8191 
8192 	can_fall_both = FALSE;
8193       }
8194 #else
8195       if (can_fall_both)
8196       {
8197 	if (game.emulation == EMU_BOULDERDASH ||
8198 	    element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8199 	  can_fall_right = FALSE;	/* slip down on left side */
8200 	else
8201 	  can_fall_left = !(can_fall_right = RND(2));
8202 
8203 	can_fall_both = FALSE;
8204       }
8205 #endif
8206 
8207       if (can_fall_any)
8208       {
8209 	/* if not determined otherwise, prefer left side for slipping down */
8210 	InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8211 	started_moving = TRUE;
8212       }
8213     }
8214 #if 0
8215     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8216 #else
8217     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8218 #endif
8219     {
8220       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8221       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8222       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8223       int belt_dir = game.belt_dir[belt_nr];
8224 
8225       if ((belt_dir == MV_LEFT  && left_is_free) ||
8226 	  (belt_dir == MV_RIGHT && right_is_free))
8227       {
8228 	int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8229 
8230 	InitMovingField(x, y, belt_dir);
8231 	started_moving = TRUE;
8232 
8233 	Pushed[x][y] = TRUE;
8234 	Pushed[nextx][y] = TRUE;
8235 
8236 	GfxAction[x][y] = ACTION_DEFAULT;
8237       }
8238       else
8239       {
8240 	MovDir[x][y] = 0;	/* if element was moving, stop it */
8241       }
8242     }
8243   }
8244 
8245   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8246 #if 0
8247   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8248 #else
8249   if (CAN_MOVE(element) && !started_moving)
8250 #endif
8251   {
8252     int move_pattern = element_info[element].move_pattern;
8253     int newx, newy;
8254 
8255 #if 0
8256 #if DEBUG
8257     if (MovDir[x][y] == MV_NONE)
8258     {
8259       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8260 	     x, y, element, element_info[element].token_name);
8261       printf("StartMoving(): This should never happen!\n");
8262     }
8263 #endif
8264 #endif
8265 
8266     Moving2Blocked(x, y, &newx, &newy);
8267 
8268     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8269       return;
8270 
8271     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8272 	CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8273     {
8274       WasJustMoving[x][y] = 0;
8275       CheckCollision[x][y] = 0;
8276 
8277       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8278 
8279       if (Feld[x][y] != element)	/* element has changed */
8280 	return;
8281     }
8282 
8283     if (!MovDelay[x][y])	/* start new movement phase */
8284     {
8285       /* all objects that can change their move direction after each step
8286 	 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8287 
8288       if (element != EL_YAMYAM &&
8289 	  element != EL_DARK_YAMYAM &&
8290 	  element != EL_PACMAN &&
8291 	  !(move_pattern & MV_ANY_DIRECTION) &&
8292 	  move_pattern != MV_TURNING_LEFT &&
8293 	  move_pattern != MV_TURNING_RIGHT &&
8294 	  move_pattern != MV_TURNING_LEFT_RIGHT &&
8295 	  move_pattern != MV_TURNING_RIGHT_LEFT &&
8296 	  move_pattern != MV_TURNING_RANDOM)
8297       {
8298 	TurnRound(x, y);
8299 
8300 	if (MovDelay[x][y] && (element == EL_BUG ||
8301 			       element == EL_SPACESHIP ||
8302 			       element == EL_SP_SNIKSNAK ||
8303 			       element == EL_SP_ELECTRON ||
8304 			       element == EL_MOLE))
8305 	  TEST_DrawLevelField(x, y);
8306       }
8307     }
8308 
8309     if (MovDelay[x][y])		/* wait some time before next movement */
8310     {
8311       MovDelay[x][y]--;
8312 
8313       if (element == EL_ROBOT ||
8314 	  element == EL_YAMYAM ||
8315 	  element == EL_DARK_YAMYAM)
8316       {
8317 	DrawLevelElementAnimationIfNeeded(x, y, element);
8318 	PlayLevelSoundAction(x, y, ACTION_WAITING);
8319       }
8320       else if (element == EL_SP_ELECTRON)
8321 	DrawLevelElementAnimationIfNeeded(x, y, element);
8322       else if (element == EL_DRAGON)
8323       {
8324 	int i;
8325 	int dir = MovDir[x][y];
8326 	int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8327 	int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8328 	int graphic = (dir == MV_LEFT	? IMG_FLAMES_1_LEFT :
8329 		       dir == MV_RIGHT	? IMG_FLAMES_1_RIGHT :
8330 		       dir == MV_UP	? IMG_FLAMES_1_UP :
8331 		       dir == MV_DOWN	? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8332 	int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8333 
8334 	GfxAction[x][y] = ACTION_ATTACKING;
8335 
8336 	if (IS_PLAYER(x, y))
8337 	  DrawPlayerField(x, y);
8338 	else
8339 	  TEST_DrawLevelField(x, y);
8340 
8341 	PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8342 
8343 	for (i = 1; i <= 3; i++)
8344 	{
8345 	  int xx = x + i * dx;
8346 	  int yy = y + i * dy;
8347 	  int sx = SCREENX(xx);
8348 	  int sy = SCREENY(yy);
8349 	  int flame_graphic = graphic + (i - 1);
8350 
8351 	  if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8352 	    break;
8353 
8354 	  if (MovDelay[x][y])
8355 	  {
8356 	    int flamed = MovingOrBlocked2Element(xx, yy);
8357 
8358 	    /* !!! */
8359 #if 0
8360 	    if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8361 	      Bang(xx, yy);
8362 	    else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8363 	      RemoveMovingField(xx, yy);
8364 	    else
8365 	      RemoveField(xx, yy);
8366 #else
8367 	    if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8368 	      Bang(xx, yy);
8369 	    else
8370 	      RemoveMovingField(xx, yy);
8371 #endif
8372 
8373 	    ChangeDelay[xx][yy] = 0;
8374 
8375 	    Feld[xx][yy] = EL_FLAMES;
8376 
8377 	    if (IN_SCR_FIELD(sx, sy))
8378 	    {
8379 	      TEST_DrawLevelFieldCrumbled(xx, yy);
8380 	      DrawGraphic(sx, sy, flame_graphic, frame);
8381 	    }
8382 	  }
8383 	  else
8384 	  {
8385 	    if (Feld[xx][yy] == EL_FLAMES)
8386 	      Feld[xx][yy] = EL_EMPTY;
8387 	    TEST_DrawLevelField(xx, yy);
8388 	  }
8389 	}
8390       }
8391 
8392       if (MovDelay[x][y])	/* element still has to wait some time */
8393       {
8394 	PlayLevelSoundAction(x, y, ACTION_WAITING);
8395 
8396 	return;
8397       }
8398     }
8399 
8400     /* now make next step */
8401 
8402     Moving2Blocked(x, y, &newx, &newy);	/* get next screen position */
8403 
8404     if (DONT_COLLIDE_WITH(element) &&
8405 	IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8406 	!PLAYER_ENEMY_PROTECTED(newx, newy))
8407     {
8408       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8409 
8410       return;
8411     }
8412 
8413     else if (CAN_MOVE_INTO_ACID(element) &&
8414 	     IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8415 	     !IS_MV_DIAGONAL(MovDir[x][y]) &&
8416 	     (MovDir[x][y] == MV_DOWN ||
8417 	      game.engine_version >= VERSION_IDENT(3,1,0,0)))
8418     {
8419       SplashAcid(newx, newy);
8420       Store[x][y] = EL_ACID;
8421     }
8422     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8423     {
8424       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8425 	  Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8426 	  Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8427 	  Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8428       {
8429 	RemoveField(x, y);
8430 	TEST_DrawLevelField(x, y);
8431 
8432 	PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8433 	if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8434 	  DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8435 
8436 	local_player->friends_still_needed--;
8437 	if (!local_player->friends_still_needed &&
8438 	    !local_player->GameOver && AllPlayersGone)
8439 	  PlayerWins(local_player);
8440 
8441 	return;
8442       }
8443       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8444       {
8445 	if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8446 	  TEST_DrawLevelField(newx, newy);
8447 	else
8448 	  GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8449       }
8450       else if (!IS_FREE(newx, newy))
8451       {
8452 	GfxAction[x][y] = ACTION_WAITING;
8453 
8454 	if (IS_PLAYER(x, y))
8455 	  DrawPlayerField(x, y);
8456 	else
8457 	  TEST_DrawLevelField(x, y);
8458 
8459 	return;
8460       }
8461     }
8462     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8463     {
8464       if (IS_FOOD_PIG(Feld[newx][newy]))
8465       {
8466 	if (IS_MOVING(newx, newy))
8467 	  RemoveMovingField(newx, newy);
8468 	else
8469 	{
8470 	  Feld[newx][newy] = EL_EMPTY;
8471 	  TEST_DrawLevelField(newx, newy);
8472 	}
8473 
8474 	PlayLevelSound(x, y, SND_PIG_DIGGING);
8475       }
8476       else if (!IS_FREE(newx, newy))
8477       {
8478 	if (IS_PLAYER(x, y))
8479 	  DrawPlayerField(x, y);
8480 	else
8481 	  TEST_DrawLevelField(x, y);
8482 
8483 	return;
8484       }
8485     }
8486     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8487     {
8488       if (Store[x][y] != EL_EMPTY)
8489       {
8490 	boolean can_clone = FALSE;
8491 	int xx, yy;
8492 
8493 	/* check if element to clone is still there */
8494 	for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8495 	{
8496 	  if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8497 	  {
8498 	    can_clone = TRUE;
8499 
8500 	    break;
8501 	  }
8502 	}
8503 
8504 	/* cannot clone or target field not free anymore -- do not clone */
8505 	if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8506 	  Store[x][y] = EL_EMPTY;
8507       }
8508 
8509       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8510       {
8511 	if (IS_MV_DIAGONAL(MovDir[x][y]))
8512 	{
8513 	  int diagonal_move_dir = MovDir[x][y];
8514 	  int stored = Store[x][y];
8515 	  int change_delay = 8;
8516 	  int graphic;
8517 
8518 	  /* android is moving diagonally */
8519 
8520 	  CreateField(x, y, EL_DIAGONAL_SHRINKING);
8521 
8522 	  Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8523 	  GfxElement[x][y] = EL_EMC_ANDROID;
8524 	  GfxAction[x][y] = ACTION_SHRINKING;
8525 	  GfxDir[x][y] = diagonal_move_dir;
8526 	  ChangeDelay[x][y] = change_delay;
8527 
8528 	  graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8529 				   GfxDir[x][y]);
8530 
8531 	  DrawLevelGraphicAnimation(x, y, graphic);
8532 	  PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8533 
8534 	  if (Feld[newx][newy] == EL_ACID)
8535 	  {
8536 	    SplashAcid(newx, newy);
8537 
8538 	    return;
8539 	  }
8540 
8541 	  CreateField(newx, newy, EL_DIAGONAL_GROWING);
8542 
8543 	  Store[newx][newy] = EL_EMC_ANDROID;
8544 	  GfxElement[newx][newy] = EL_EMC_ANDROID;
8545 	  GfxAction[newx][newy] = ACTION_GROWING;
8546 	  GfxDir[newx][newy] = diagonal_move_dir;
8547 	  ChangeDelay[newx][newy] = change_delay;
8548 
8549 	  graphic = el_act_dir2img(GfxElement[newx][newy],
8550 				   GfxAction[newx][newy], GfxDir[newx][newy]);
8551 
8552 	  DrawLevelGraphicAnimation(newx, newy, graphic);
8553 	  PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8554 
8555 	  return;
8556 	}
8557 	else
8558 	{
8559 	  Feld[newx][newy] = EL_EMPTY;
8560 	  TEST_DrawLevelField(newx, newy);
8561 
8562 	  PlayLevelSoundAction(x, y, ACTION_DIGGING);
8563 	}
8564       }
8565       else if (!IS_FREE(newx, newy))
8566       {
8567 #if 0
8568 	if (IS_PLAYER(x, y))
8569 	  DrawPlayerField(x, y);
8570 	else
8571 	  TEST_DrawLevelField(x, y);
8572 #endif
8573 
8574 	return;
8575       }
8576     }
8577     else if (IS_CUSTOM_ELEMENT(element) &&
8578 	     CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8579     {
8580 #if 1
8581       if (!DigFieldByCE(newx, newy, element))
8582 	return;
8583 #else
8584       int new_element = Feld[newx][newy];
8585 
8586       if (!IS_FREE(newx, newy))
8587       {
8588 	int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8589 		      IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8590 		      ACTION_BREAKING);
8591 
8592 	/* no element can dig solid indestructible elements */
8593 	if (IS_INDESTRUCTIBLE(new_element) &&
8594 	    !IS_DIGGABLE(new_element) &&
8595 	    !IS_COLLECTIBLE(new_element))
8596 	  return;
8597 
8598 	if (AmoebaNr[newx][newy] &&
8599 	    (new_element == EL_AMOEBA_FULL ||
8600 	     new_element == EL_BD_AMOEBA ||
8601 	     new_element == EL_AMOEBA_GROWING))
8602 	{
8603 	  AmoebaCnt[AmoebaNr[newx][newy]]--;
8604 	  AmoebaCnt2[AmoebaNr[newx][newy]]--;
8605 	}
8606 
8607 	if (IS_MOVING(newx, newy))
8608 	  RemoveMovingField(newx, newy);
8609 	else
8610 	{
8611 	  RemoveField(newx, newy);
8612 	  TEST_DrawLevelField(newx, newy);
8613 	}
8614 
8615 	/* if digged element was about to explode, prevent the explosion */
8616 	ExplodeField[newx][newy] = EX_TYPE_NONE;
8617 
8618 	PlayLevelSoundAction(x, y, action);
8619       }
8620 
8621       Store[newx][newy] = EL_EMPTY;
8622 
8623 #if 1
8624       /* this makes it possible to leave the removed element again */
8625       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8626 	Store[newx][newy] = new_element;
8627 #else
8628       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8629       {
8630 	int move_leave_element = element_info[element].move_leave_element;
8631 
8632 	/* this makes it possible to leave the removed element again */
8633 	Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8634 			     new_element : move_leave_element);
8635       }
8636 #endif
8637 
8638 #endif
8639 
8640       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8641       {
8642 	RunnerVisit[x][y] = FrameCounter;
8643 	PlayerVisit[x][y] /= 8;		/* expire player visit path */
8644       }
8645     }
8646     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8647     {
8648       if (!IS_FREE(newx, newy))
8649       {
8650 	if (IS_PLAYER(x, y))
8651 	  DrawPlayerField(x, y);
8652 	else
8653 	  TEST_DrawLevelField(x, y);
8654 
8655 	return;
8656       }
8657       else
8658       {
8659 	boolean wanna_flame = !RND(10);
8660 	int dx = newx - x, dy = newy - y;
8661 	int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8662 	int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8663 	int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8664 			MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8665 	int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8666 			MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8667 
8668 	if ((wanna_flame ||
8669 	     IS_CLASSIC_ENEMY(element1) ||
8670 	     IS_CLASSIC_ENEMY(element2)) &&
8671 	    element1 != EL_DRAGON && element2 != EL_DRAGON &&
8672 	    element1 != EL_FLAMES && element2 != EL_FLAMES)
8673 	{
8674 	  ResetGfxAnimation(x, y);
8675 	  GfxAction[x][y] = ACTION_ATTACKING;
8676 
8677 	  if (IS_PLAYER(x, y))
8678 	    DrawPlayerField(x, y);
8679 	  else
8680 	    TEST_DrawLevelField(x, y);
8681 
8682 	  PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8683 
8684 	  MovDelay[x][y] = 50;
8685 
8686 	  /* !!! */
8687 #if 0
8688 	  RemoveField(newx, newy);
8689 #endif
8690 	  Feld[newx][newy] = EL_FLAMES;
8691 	  if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8692 	  {
8693 #if 0
8694 	    RemoveField(newx1, newy1);
8695 #endif
8696 	    Feld[newx1][newy1] = EL_FLAMES;
8697 	  }
8698 	  if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8699 	  {
8700 #if 0
8701 	    RemoveField(newx2, newy2);
8702 #endif
8703 	    Feld[newx2][newy2] = EL_FLAMES;
8704 	  }
8705 
8706 	  return;
8707 	}
8708       }
8709     }
8710     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8711 	     Feld[newx][newy] == EL_DIAMOND)
8712     {
8713       if (IS_MOVING(newx, newy))
8714 	RemoveMovingField(newx, newy);
8715       else
8716       {
8717 	Feld[newx][newy] = EL_EMPTY;
8718 	TEST_DrawLevelField(newx, newy);
8719       }
8720 
8721       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8722     }
8723     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8724 	     IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8725     {
8726       if (AmoebaNr[newx][newy])
8727       {
8728 	AmoebaCnt2[AmoebaNr[newx][newy]]--;
8729 	if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8730 	    Feld[newx][newy] == EL_BD_AMOEBA)
8731 	  AmoebaCnt[AmoebaNr[newx][newy]]--;
8732       }
8733 
8734 #if 0
8735       /* !!! test !!! */
8736       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8737       {
8738 	RemoveMovingField(newx, newy);
8739       }
8740 #else
8741       if (IS_MOVING(newx, newy))
8742       {
8743 	RemoveMovingField(newx, newy);
8744       }
8745 #endif
8746       else
8747       {
8748 	Feld[newx][newy] = EL_EMPTY;
8749 	TEST_DrawLevelField(newx, newy);
8750       }
8751 
8752       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8753     }
8754     else if ((element == EL_PACMAN || element == EL_MOLE)
8755 	     && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8756     {
8757       if (AmoebaNr[newx][newy])
8758       {
8759 	AmoebaCnt2[AmoebaNr[newx][newy]]--;
8760 	if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8761 	    Feld[newx][newy] == EL_BD_AMOEBA)
8762 	  AmoebaCnt[AmoebaNr[newx][newy]]--;
8763       }
8764 
8765       if (element == EL_MOLE)
8766       {
8767 	Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8768 	PlayLevelSound(x, y, SND_MOLE_DIGGING);
8769 
8770 	ResetGfxAnimation(x, y);
8771 	GfxAction[x][y] = ACTION_DIGGING;
8772 	TEST_DrawLevelField(x, y);
8773 
8774 	MovDelay[newx][newy] = 0;	/* start amoeba shrinking delay */
8775 
8776 	return;				/* wait for shrinking amoeba */
8777       }
8778       else	/* element == EL_PACMAN */
8779       {
8780 	Feld[newx][newy] = EL_EMPTY;
8781 	TEST_DrawLevelField(newx, newy);
8782 	PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8783       }
8784     }
8785     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8786 	     (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8787 	      (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8788     {
8789       /* wait for shrinking amoeba to completely disappear */
8790       return;
8791     }
8792     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8793     {
8794       /* object was running against a wall */
8795 
8796       TurnRound(x, y);
8797 
8798 #if 0
8799       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8800       if (move_pattern & MV_ANY_DIRECTION &&
8801 	  move_pattern == MovDir[x][y])
8802       {
8803 	int blocking_element =
8804 	  (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8805 
8806 	CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8807 				 MovDir[x][y]);
8808 
8809 	element = Feld[x][y];	/* element might have changed */
8810       }
8811 #endif
8812 
8813       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8814 	DrawLevelElementAnimation(x, y, element);
8815 
8816       if (DONT_TOUCH(element))
8817 	TestIfBadThingTouchesPlayer(x, y);
8818 
8819       return;
8820     }
8821 
8822     InitMovingField(x, y, MovDir[x][y]);
8823 
8824     PlayLevelSoundAction(x, y, ACTION_MOVING);
8825   }
8826 
8827   if (MovDir[x][y])
8828     ContinueMoving(x, y);
8829 }
8830 
ContinueMoving(int x,int y)8831 void ContinueMoving(int x, int y)
8832 {
8833   int element = Feld[x][y];
8834   struct ElementInfo *ei = &element_info[element];
8835   int direction = MovDir[x][y];
8836   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8837   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8838   int newx = x + dx, newy = y + dy;
8839   int stored = Store[x][y];
8840   int stored_new = Store[newx][newy];
8841   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8842   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8843   boolean last_line = (newy == lev_fieldy - 1);
8844 
8845   MovPos[x][y] += getElementMoveStepsize(x, y);
8846 
8847   if (pushed_by_player)	/* special case: moving object pushed by player */
8848     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8849 
8850   if (ABS(MovPos[x][y]) < TILEX)
8851   {
8852 #if 0
8853     int ee = Feld[x][y];
8854     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8855     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8856 
8857     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8858 	   x, y, ABS(MovPos[x][y]),
8859 	   ee, gg, ff,
8860 	   GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8861 #endif
8862 
8863     TEST_DrawLevelField(x, y);
8864 
8865     return;	/* element is still moving */
8866   }
8867 
8868   /* element reached destination field */
8869 
8870   Feld[x][y] = EL_EMPTY;
8871   Feld[newx][newy] = element;
8872   MovPos[x][y] = 0;	/* force "not moving" for "crumbled sand" */
8873 
8874   if (Store[x][y] == EL_ACID)	/* element is moving into acid pool */
8875   {
8876     element = Feld[newx][newy] = EL_ACID;
8877   }
8878   else if (element == EL_MOLE)
8879   {
8880     Feld[x][y] = EL_SAND;
8881 
8882     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8883   }
8884   else if (element == EL_QUICKSAND_FILLING)
8885   {
8886     element = Feld[newx][newy] = get_next_element(element);
8887     Store[newx][newy] = Store[x][y];
8888   }
8889   else if (element == EL_QUICKSAND_EMPTYING)
8890   {
8891     Feld[x][y] = get_next_element(element);
8892     element = Feld[newx][newy] = Store[x][y];
8893   }
8894   else if (element == EL_QUICKSAND_FAST_FILLING)
8895   {
8896     element = Feld[newx][newy] = get_next_element(element);
8897     Store[newx][newy] = Store[x][y];
8898   }
8899   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8900   {
8901     Feld[x][y] = get_next_element(element);
8902     element = Feld[newx][newy] = Store[x][y];
8903   }
8904   else if (element == EL_MAGIC_WALL_FILLING)
8905   {
8906     element = Feld[newx][newy] = get_next_element(element);
8907     if (!game.magic_wall_active)
8908       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8909     Store[newx][newy] = Store[x][y];
8910   }
8911   else if (element == EL_MAGIC_WALL_EMPTYING)
8912   {
8913     Feld[x][y] = get_next_element(element);
8914     if (!game.magic_wall_active)
8915       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8916     element = Feld[newx][newy] = Store[x][y];
8917 
8918 #if USE_NEW_CUSTOM_VALUE
8919     InitField(newx, newy, FALSE);
8920 #endif
8921   }
8922   else if (element == EL_BD_MAGIC_WALL_FILLING)
8923   {
8924     element = Feld[newx][newy] = get_next_element(element);
8925     if (!game.magic_wall_active)
8926       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8927     Store[newx][newy] = Store[x][y];
8928   }
8929   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8930   {
8931     Feld[x][y] = get_next_element(element);
8932     if (!game.magic_wall_active)
8933       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8934     element = Feld[newx][newy] = Store[x][y];
8935 
8936 #if USE_NEW_CUSTOM_VALUE
8937     InitField(newx, newy, FALSE);
8938 #endif
8939   }
8940   else if (element == EL_DC_MAGIC_WALL_FILLING)
8941   {
8942     element = Feld[newx][newy] = get_next_element(element);
8943     if (!game.magic_wall_active)
8944       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8945     Store[newx][newy] = Store[x][y];
8946   }
8947   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8948   {
8949     Feld[x][y] = get_next_element(element);
8950     if (!game.magic_wall_active)
8951       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8952     element = Feld[newx][newy] = Store[x][y];
8953 
8954 #if USE_NEW_CUSTOM_VALUE
8955     InitField(newx, newy, FALSE);
8956 #endif
8957   }
8958   else if (element == EL_AMOEBA_DROPPING)
8959   {
8960     Feld[x][y] = get_next_element(element);
8961     element = Feld[newx][newy] = Store[x][y];
8962   }
8963   else if (element == EL_SOKOBAN_OBJECT)
8964   {
8965     if (Back[x][y])
8966       Feld[x][y] = Back[x][y];
8967 
8968     if (Back[newx][newy])
8969       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8970 
8971     Back[x][y] = Back[newx][newy] = 0;
8972   }
8973 
8974   Store[x][y] = EL_EMPTY;
8975   MovPos[x][y] = 0;
8976   MovDir[x][y] = 0;
8977   MovDelay[x][y] = 0;
8978 
8979   MovDelay[newx][newy] = 0;
8980 
8981   if (CAN_CHANGE_OR_HAS_ACTION(element))
8982   {
8983     /* copy element change control values to new field */
8984     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8985     ChangePage[newx][newy]  = ChangePage[x][y];
8986     ChangeCount[newx][newy] = ChangeCount[x][y];
8987     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8988   }
8989 
8990 #if USE_NEW_CUSTOM_VALUE
8991   CustomValue[newx][newy] = CustomValue[x][y];
8992 #endif
8993 
8994   ChangeDelay[x][y] = 0;
8995   ChangePage[x][y] = -1;
8996   ChangeCount[x][y] = 0;
8997   ChangeEvent[x][y] = -1;
8998 
8999 #if USE_NEW_CUSTOM_VALUE
9000   CustomValue[x][y] = 0;
9001 #endif
9002 
9003   /* copy animation control values to new field */
9004   GfxFrame[newx][newy]  = GfxFrame[x][y];
9005   GfxRandom[newx][newy] = GfxRandom[x][y];	/* keep same random value */
9006   GfxAction[newx][newy] = GfxAction[x][y];	/* keep action one frame  */
9007   GfxDir[newx][newy]    = GfxDir[x][y];		/* keep element direction */
9008 
9009   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9010 
9011   /* some elements can leave other elements behind after moving */
9012 #if 1
9013   if (ei->move_leave_element != EL_EMPTY &&
9014       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9015       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9016 #else
9017   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9018       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9019       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9020 #endif
9021   {
9022     int move_leave_element = ei->move_leave_element;
9023 
9024 #if 1
9025 #if 1
9026     /* this makes it possible to leave the removed element again */
9027     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9028       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9029 #else
9030     /* this makes it possible to leave the removed element again */
9031     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9032       move_leave_element = stored;
9033 #endif
9034 #else
9035     /* this makes it possible to leave the removed element again */
9036     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9037         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9038       move_leave_element = stored;
9039 #endif
9040 
9041     Feld[x][y] = move_leave_element;
9042 
9043     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9044       MovDir[x][y] = direction;
9045 
9046     InitField(x, y, FALSE);
9047 
9048     if (GFX_CRUMBLED(Feld[x][y]))
9049       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9050 
9051     if (ELEM_IS_PLAYER(move_leave_element))
9052       RelocatePlayer(x, y, move_leave_element);
9053   }
9054 
9055   /* do this after checking for left-behind element */
9056   ResetGfxAnimation(x, y);	/* reset animation values for old field */
9057 
9058   if (!CAN_MOVE(element) ||
9059       (CAN_FALL(element) && direction == MV_DOWN &&
9060        (element == EL_SPRING ||
9061 	element_info[element].move_pattern == MV_WHEN_PUSHED ||
9062 	element_info[element].move_pattern == MV_WHEN_DROPPED)))
9063     GfxDir[x][y] = MovDir[newx][newy] = 0;
9064 
9065   TEST_DrawLevelField(x, y);
9066   TEST_DrawLevelField(newx, newy);
9067 
9068   Stop[newx][newy] = TRUE;	/* ignore this element until the next frame */
9069 
9070   /* prevent pushed element from moving on in pushed direction */
9071   if (pushed_by_player && CAN_MOVE(element) &&
9072       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9073       !(element_info[element].move_pattern & direction))
9074     TurnRound(newx, newy);
9075 
9076   /* prevent elements on conveyor belt from moving on in last direction */
9077   if (pushed_by_conveyor && CAN_FALL(element) &&
9078       direction & MV_HORIZONTAL)
9079     MovDir[newx][newy] = 0;
9080 
9081   if (!pushed_by_player)
9082   {
9083     int nextx = newx + dx, nexty = newy + dy;
9084     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9085 
9086     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9087 
9088     if (CAN_FALL(element) && direction == MV_DOWN)
9089       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9090 
9091     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9092       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9093 
9094 #if USE_FIX_IMPACT_COLLISION
9095     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9096       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9097 #endif
9098   }
9099 
9100   if (DONT_TOUCH(element))	/* object may be nasty to player or others */
9101   {
9102     TestIfBadThingTouchesPlayer(newx, newy);
9103     TestIfBadThingTouchesFriend(newx, newy);
9104 
9105     if (!IS_CUSTOM_ELEMENT(element))
9106       TestIfBadThingTouchesOtherBadThing(newx, newy);
9107   }
9108   else if (element == EL_PENGUIN)
9109     TestIfFriendTouchesBadThing(newx, newy);
9110 
9111   if (DONT_GET_HIT_BY(element))
9112   {
9113     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9114   }
9115 
9116   /* give the player one last chance (one more frame) to move away */
9117   if (CAN_FALL(element) && direction == MV_DOWN &&
9118       (last_line || (!IS_FREE(x, newy + 1) &&
9119 		     (!IS_PLAYER(x, newy + 1) ||
9120 		      game.engine_version < VERSION_IDENT(3,1,1,0)))))
9121     Impact(x, newy);
9122 
9123   if (pushed_by_player && !game.use_change_when_pushing_bug)
9124   {
9125     int push_side = MV_DIR_OPPOSITE(direction);
9126     struct PlayerInfo *player = PLAYERINFO(x, y);
9127 
9128     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9129 			       player->index_bit, push_side);
9130     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9131 					player->index_bit, push_side);
9132   }
9133 
9134   if (element == EL_EMC_ANDROID && pushed_by_player)	/* make another move */
9135     MovDelay[newx][newy] = 1;
9136 
9137   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9138 
9139   TestIfElementTouchesCustomElement(x, y);	/* empty or new element */
9140 
9141 #if 0
9142   if (ChangePage[newx][newy] != -1)		/* delayed change */
9143   {
9144     int page = ChangePage[newx][newy];
9145     struct ElementChangeInfo *change = &ei->change_page[page];
9146 
9147     ChangePage[newx][newy] = -1;
9148 
9149     if (change->can_change)
9150     {
9151       if (ChangeElement(newx, newy, element, page))
9152       {
9153         if (change->post_change_function)
9154           change->post_change_function(newx, newy);
9155       }
9156     }
9157 
9158     if (change->has_action)
9159       ExecuteCustomElementAction(newx, newy, element, page);
9160   }
9161 #endif
9162 
9163   TestIfElementHitsCustomElement(newx, newy, direction);
9164   TestIfPlayerTouchesCustomElement(newx, newy);
9165   TestIfElementTouchesCustomElement(newx, newy);
9166 
9167   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9168       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9169     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9170 			     MV_DIR_OPPOSITE(direction));
9171 }
9172 
AmoebeNachbarNr(int ax,int ay)9173 int AmoebeNachbarNr(int ax, int ay)
9174 {
9175   int i;
9176   int element = Feld[ax][ay];
9177   int group_nr = 0;
9178   static int xy[4][2] =
9179   {
9180     { 0, -1 },
9181     { -1, 0 },
9182     { +1, 0 },
9183     { 0, +1 }
9184   };
9185 
9186   for (i = 0; i < NUM_DIRECTIONS; i++)
9187   {
9188     int x = ax + xy[i][0];
9189     int y = ay + xy[i][1];
9190 
9191     if (!IN_LEV_FIELD(x, y))
9192       continue;
9193 
9194     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9195       group_nr = AmoebaNr[x][y];
9196   }
9197 
9198   return group_nr;
9199 }
9200 
AmoebenVereinigen(int ax,int ay)9201 void AmoebenVereinigen(int ax, int ay)
9202 {
9203   int i, x, y, xx, yy;
9204   int new_group_nr = AmoebaNr[ax][ay];
9205   static int xy[4][2] =
9206   {
9207     { 0, -1 },
9208     { -1, 0 },
9209     { +1, 0 },
9210     { 0, +1 }
9211   };
9212 
9213   if (new_group_nr == 0)
9214     return;
9215 
9216   for (i = 0; i < NUM_DIRECTIONS; i++)
9217   {
9218     x = ax + xy[i][0];
9219     y = ay + xy[i][1];
9220 
9221     if (!IN_LEV_FIELD(x, y))
9222       continue;
9223 
9224     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9225 	 Feld[x][y] == EL_BD_AMOEBA ||
9226 	 Feld[x][y] == EL_AMOEBA_DEAD) &&
9227 	AmoebaNr[x][y] != new_group_nr)
9228     {
9229       int old_group_nr = AmoebaNr[x][y];
9230 
9231       if (old_group_nr == 0)
9232 	return;
9233 
9234       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9235       AmoebaCnt[old_group_nr] = 0;
9236       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9237       AmoebaCnt2[old_group_nr] = 0;
9238 
9239       SCAN_PLAYFIELD(xx, yy)
9240       {
9241 	if (AmoebaNr[xx][yy] == old_group_nr)
9242 	  AmoebaNr[xx][yy] = new_group_nr;
9243       }
9244     }
9245   }
9246 }
9247 
AmoebeUmwandeln(int ax,int ay)9248 void AmoebeUmwandeln(int ax, int ay)
9249 {
9250   int i, x, y;
9251 
9252   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9253   {
9254     int group_nr = AmoebaNr[ax][ay];
9255 
9256 #ifdef DEBUG
9257     if (group_nr == 0)
9258     {
9259       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9260       printf("AmoebeUmwandeln(): This should never happen!\n");
9261       return;
9262     }
9263 #endif
9264 
9265     SCAN_PLAYFIELD(x, y)
9266     {
9267       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9268       {
9269 	AmoebaNr[x][y] = 0;
9270 	Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9271       }
9272     }
9273 
9274     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9275 			    SND_AMOEBA_TURNING_TO_GEM :
9276 			    SND_AMOEBA_TURNING_TO_ROCK));
9277     Bang(ax, ay);
9278   }
9279   else
9280   {
9281     static int xy[4][2] =
9282     {
9283       { 0, -1 },
9284       { -1, 0 },
9285       { +1, 0 },
9286       { 0, +1 }
9287     };
9288 
9289     for (i = 0; i < NUM_DIRECTIONS; i++)
9290     {
9291       x = ax + xy[i][0];
9292       y = ay + xy[i][1];
9293 
9294       if (!IN_LEV_FIELD(x, y))
9295 	continue;
9296 
9297       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9298       {
9299 	PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9300 			      SND_AMOEBA_TURNING_TO_GEM :
9301 			      SND_AMOEBA_TURNING_TO_ROCK));
9302 	Bang(x, y);
9303       }
9304     }
9305   }
9306 }
9307 
AmoebeUmwandelnBD(int ax,int ay,int new_element)9308 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9309 {
9310   int x, y;
9311   int group_nr = AmoebaNr[ax][ay];
9312   boolean done = FALSE;
9313 
9314 #ifdef DEBUG
9315   if (group_nr == 0)
9316   {
9317     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9318     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9319     return;
9320   }
9321 #endif
9322 
9323   SCAN_PLAYFIELD(x, y)
9324   {
9325     if (AmoebaNr[x][y] == group_nr &&
9326 	(Feld[x][y] == EL_AMOEBA_DEAD ||
9327 	 Feld[x][y] == EL_BD_AMOEBA ||
9328 	 Feld[x][y] == EL_AMOEBA_GROWING))
9329     {
9330       AmoebaNr[x][y] = 0;
9331       Feld[x][y] = new_element;
9332       InitField(x, y, FALSE);
9333       TEST_DrawLevelField(x, y);
9334       done = TRUE;
9335     }
9336   }
9337 
9338   if (done)
9339     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9340 			    SND_BD_AMOEBA_TURNING_TO_ROCK :
9341 			    SND_BD_AMOEBA_TURNING_TO_GEM));
9342 }
9343 
AmoebeWaechst(int x,int y)9344 void AmoebeWaechst(int x, int y)
9345 {
9346   static unsigned int sound_delay = 0;
9347   static unsigned int sound_delay_value = 0;
9348 
9349   if (!MovDelay[x][y])		/* start new growing cycle */
9350   {
9351     MovDelay[x][y] = 7;
9352 
9353     if (DelayReached(&sound_delay, sound_delay_value))
9354     {
9355       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9356       sound_delay_value = 30;
9357     }
9358   }
9359 
9360   if (MovDelay[x][y])		/* wait some time before growing bigger */
9361   {
9362     MovDelay[x][y]--;
9363     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9364     {
9365       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9366 					   6 - MovDelay[x][y]);
9367 
9368       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9369     }
9370 
9371     if (!MovDelay[x][y])
9372     {
9373       Feld[x][y] = Store[x][y];
9374       Store[x][y] = 0;
9375       TEST_DrawLevelField(x, y);
9376     }
9377   }
9378 }
9379 
AmoebaDisappearing(int x,int y)9380 void AmoebaDisappearing(int x, int y)
9381 {
9382   static unsigned int sound_delay = 0;
9383   static unsigned int sound_delay_value = 0;
9384 
9385   if (!MovDelay[x][y])		/* start new shrinking cycle */
9386   {
9387     MovDelay[x][y] = 7;
9388 
9389     if (DelayReached(&sound_delay, sound_delay_value))
9390       sound_delay_value = 30;
9391   }
9392 
9393   if (MovDelay[x][y])		/* wait some time before shrinking */
9394   {
9395     MovDelay[x][y]--;
9396     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9397     {
9398       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9399 					   6 - MovDelay[x][y]);
9400 
9401       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9402     }
9403 
9404     if (!MovDelay[x][y])
9405     {
9406       Feld[x][y] = EL_EMPTY;
9407       TEST_DrawLevelField(x, y);
9408 
9409       /* don't let mole enter this field in this cycle;
9410 	 (give priority to objects falling to this field from above) */
9411       Stop[x][y] = TRUE;
9412     }
9413   }
9414 }
9415 
AmoebeAbleger(int ax,int ay)9416 void AmoebeAbleger(int ax, int ay)
9417 {
9418   int i;
9419   int element = Feld[ax][ay];
9420   int graphic = el2img(element);
9421   int newax = ax, neway = ay;
9422   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9423   static int xy[4][2] =
9424   {
9425     { 0, -1 },
9426     { -1, 0 },
9427     { +1, 0 },
9428     { 0, +1 }
9429   };
9430 
9431   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9432   {
9433     Feld[ax][ay] = EL_AMOEBA_DEAD;
9434     TEST_DrawLevelField(ax, ay);
9435     return;
9436   }
9437 
9438   if (IS_ANIMATED(graphic))
9439     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9440 
9441   if (!MovDelay[ax][ay])	/* start making new amoeba field */
9442     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9443 
9444   if (MovDelay[ax][ay])		/* wait some time before making new amoeba */
9445   {
9446     MovDelay[ax][ay]--;
9447     if (MovDelay[ax][ay])
9448       return;
9449   }
9450 
9451   if (can_drop)			/* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9452   {
9453     int start = RND(4);
9454     int x = ax + xy[start][0];
9455     int y = ay + xy[start][1];
9456 
9457     if (!IN_LEV_FIELD(x, y))
9458       return;
9459 
9460     if (IS_FREE(x, y) ||
9461 	CAN_GROW_INTO(Feld[x][y]) ||
9462 	Feld[x][y] == EL_QUICKSAND_EMPTY ||
9463 	Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9464     {
9465       newax = x;
9466       neway = y;
9467     }
9468 
9469     if (newax == ax && neway == ay)
9470       return;
9471   }
9472   else				/* normal or "filled" (BD style) amoeba */
9473   {
9474     int start = RND(4);
9475     boolean waiting_for_player = FALSE;
9476 
9477     for (i = 0; i < NUM_DIRECTIONS; i++)
9478     {
9479       int j = (start + i) % 4;
9480       int x = ax + xy[j][0];
9481       int y = ay + xy[j][1];
9482 
9483       if (!IN_LEV_FIELD(x, y))
9484 	continue;
9485 
9486       if (IS_FREE(x, y) ||
9487 	  CAN_GROW_INTO(Feld[x][y]) ||
9488 	  Feld[x][y] == EL_QUICKSAND_EMPTY ||
9489 	  Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9490       {
9491 	newax = x;
9492 	neway = y;
9493 	break;
9494       }
9495       else if (IS_PLAYER(x, y))
9496 	waiting_for_player = TRUE;
9497     }
9498 
9499     if (newax == ax && neway == ay)		/* amoeba cannot grow */
9500     {
9501       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9502       {
9503 	Feld[ax][ay] = EL_AMOEBA_DEAD;
9504 	TEST_DrawLevelField(ax, ay);
9505 	AmoebaCnt[AmoebaNr[ax][ay]]--;
9506 
9507 	if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)	/* amoeba is completely dead */
9508 	{
9509 	  if (element == EL_AMOEBA_FULL)
9510 	    AmoebeUmwandeln(ax, ay);
9511 	  else if (element == EL_BD_AMOEBA)
9512 	    AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9513 	}
9514       }
9515       return;
9516     }
9517     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9518     {
9519       /* amoeba gets larger by growing in some direction */
9520 
9521       int new_group_nr = AmoebaNr[ax][ay];
9522 
9523 #ifdef DEBUG
9524   if (new_group_nr == 0)
9525   {
9526     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9527     printf("AmoebeAbleger(): This should never happen!\n");
9528     return;
9529   }
9530 #endif
9531 
9532       AmoebaNr[newax][neway] = new_group_nr;
9533       AmoebaCnt[new_group_nr]++;
9534       AmoebaCnt2[new_group_nr]++;
9535 
9536       /* if amoeba touches other amoeba(s) after growing, unify them */
9537       AmoebenVereinigen(newax, neway);
9538 
9539       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9540       {
9541 	AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9542 	return;
9543       }
9544     }
9545   }
9546 
9547   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9548       (neway == lev_fieldy - 1 && newax != ax))
9549   {
9550     Feld[newax][neway] = EL_AMOEBA_GROWING;	/* creation of new amoeba */
9551     Store[newax][neway] = element;
9552   }
9553   else if (neway == ay || element == EL_EMC_DRIPPER)
9554   {
9555     Feld[newax][neway] = EL_AMOEBA_DROP;	/* drop left/right of amoeba */
9556 
9557     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9558   }
9559   else
9560   {
9561     InitMovingField(ax, ay, MV_DOWN);		/* drop dripping from amoeba */
9562     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9563     Store[ax][ay] = EL_AMOEBA_DROP;
9564     ContinueMoving(ax, ay);
9565     return;
9566   }
9567 
9568   TEST_DrawLevelField(newax, neway);
9569 }
9570 
Life(int ax,int ay)9571 void Life(int ax, int ay)
9572 {
9573   int x1, y1, x2, y2;
9574   int life_time = 40;
9575   int element = Feld[ax][ay];
9576   int graphic = el2img(element);
9577   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9578 			 level.biomaze);
9579   boolean changed = FALSE;
9580 
9581   if (IS_ANIMATED(graphic))
9582     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9583 
9584   if (Stop[ax][ay])
9585     return;
9586 
9587   if (!MovDelay[ax][ay])	/* start new "game of life" cycle */
9588     MovDelay[ax][ay] = life_time;
9589 
9590   if (MovDelay[ax][ay])		/* wait some time before next cycle */
9591   {
9592     MovDelay[ax][ay]--;
9593     if (MovDelay[ax][ay])
9594       return;
9595   }
9596 
9597   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9598   {
9599     int xx = ax+x1, yy = ay+y1;
9600     int nachbarn = 0;
9601 
9602     if (!IN_LEV_FIELD(xx, yy))
9603       continue;
9604 
9605     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9606     {
9607       int x = xx+x2, y = yy+y2;
9608 
9609       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9610 	continue;
9611 
9612       if (((Feld[x][y] == element ||
9613 	    (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9614 	   !Stop[x][y]) ||
9615 	  (IS_FREE(x, y) && Stop[x][y]))
9616 	nachbarn++;
9617     }
9618 
9619     if (xx == ax && yy == ay)		/* field in the middle */
9620     {
9621       if (nachbarn < life_parameter[0] ||
9622 	  nachbarn > life_parameter[1])
9623       {
9624 	Feld[xx][yy] = EL_EMPTY;
9625 	if (!Stop[xx][yy])
9626 	  TEST_DrawLevelField(xx, yy);
9627 	Stop[xx][yy] = TRUE;
9628 	changed = TRUE;
9629       }
9630     }
9631     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9632     {					/* free border field */
9633       if (nachbarn >= life_parameter[2] &&
9634 	  nachbarn <= life_parameter[3])
9635       {
9636 	Feld[xx][yy] = element;
9637 	MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9638 	if (!Stop[xx][yy])
9639 	  TEST_DrawLevelField(xx, yy);
9640 	Stop[xx][yy] = TRUE;
9641 	changed = TRUE;
9642       }
9643     }
9644   }
9645 
9646   if (changed)
9647     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9648 		   SND_GAME_OF_LIFE_GROWING);
9649 }
9650 
InitRobotWheel(int x,int y)9651 static void InitRobotWheel(int x, int y)
9652 {
9653   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9654 }
9655 
RunRobotWheel(int x,int y)9656 static void RunRobotWheel(int x, int y)
9657 {
9658   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9659 }
9660 
StopRobotWheel(int x,int y)9661 static void StopRobotWheel(int x, int y)
9662 {
9663   if (ZX == x && ZY == y)
9664   {
9665     ZX = ZY = -1;
9666 
9667     game.robot_wheel_active = FALSE;
9668   }
9669 }
9670 
InitTimegateWheel(int x,int y)9671 static void InitTimegateWheel(int x, int y)
9672 {
9673   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9674 }
9675 
RunTimegateWheel(int x,int y)9676 static void RunTimegateWheel(int x, int y)
9677 {
9678   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9679 }
9680 
InitMagicBallDelay(int x,int y)9681 static void InitMagicBallDelay(int x, int y)
9682 {
9683 #if 1
9684   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9685 #else
9686   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9687 #endif
9688 }
9689 
ActivateMagicBall(int bx,int by)9690 static void ActivateMagicBall(int bx, int by)
9691 {
9692   int x, y;
9693 
9694   if (level.ball_random)
9695   {
9696     int pos_border = RND(8);	/* select one of the eight border elements */
9697     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9698     int xx = pos_content % 3;
9699     int yy = pos_content / 3;
9700 
9701     x = bx - 1 + xx;
9702     y = by - 1 + yy;
9703 
9704     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9705       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9706   }
9707   else
9708   {
9709     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9710     {
9711       int xx = x - bx + 1;
9712       int yy = y - by + 1;
9713 
9714       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9715 	CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9716     }
9717   }
9718 
9719   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9720 }
9721 
CheckExit(int x,int y)9722 void CheckExit(int x, int y)
9723 {
9724   if (local_player->gems_still_needed > 0 ||
9725       local_player->sokobanfields_still_needed > 0 ||
9726       local_player->lights_still_needed > 0)
9727   {
9728     int element = Feld[x][y];
9729     int graphic = el2img(element);
9730 
9731     if (IS_ANIMATED(graphic))
9732       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9733 
9734     return;
9735   }
9736 
9737   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9738     return;
9739 
9740   Feld[x][y] = EL_EXIT_OPENING;
9741 
9742   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9743 }
9744 
CheckExitEM(int x,int y)9745 void CheckExitEM(int x, int y)
9746 {
9747   if (local_player->gems_still_needed > 0 ||
9748       local_player->sokobanfields_still_needed > 0 ||
9749       local_player->lights_still_needed > 0)
9750   {
9751     int element = Feld[x][y];
9752     int graphic = el2img(element);
9753 
9754     if (IS_ANIMATED(graphic))
9755       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9756 
9757     return;
9758   }
9759 
9760   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9761     return;
9762 
9763   Feld[x][y] = EL_EM_EXIT_OPENING;
9764 
9765   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9766 }
9767 
CheckExitSteel(int x,int y)9768 void CheckExitSteel(int x, int y)
9769 {
9770   if (local_player->gems_still_needed > 0 ||
9771       local_player->sokobanfields_still_needed > 0 ||
9772       local_player->lights_still_needed > 0)
9773   {
9774     int element = Feld[x][y];
9775     int graphic = el2img(element);
9776 
9777     if (IS_ANIMATED(graphic))
9778       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9779 
9780     return;
9781   }
9782 
9783   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9784     return;
9785 
9786   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9787 
9788   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9789 }
9790 
CheckExitSteelEM(int x,int y)9791 void CheckExitSteelEM(int x, int y)
9792 {
9793   if (local_player->gems_still_needed > 0 ||
9794       local_player->sokobanfields_still_needed > 0 ||
9795       local_player->lights_still_needed > 0)
9796   {
9797     int element = Feld[x][y];
9798     int graphic = el2img(element);
9799 
9800     if (IS_ANIMATED(graphic))
9801       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9802 
9803     return;
9804   }
9805 
9806   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9807     return;
9808 
9809   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9810 
9811   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9812 }
9813 
CheckExitSP(int x,int y)9814 void CheckExitSP(int x, int y)
9815 {
9816   if (local_player->gems_still_needed > 0)
9817   {
9818     int element = Feld[x][y];
9819     int graphic = el2img(element);
9820 
9821     if (IS_ANIMATED(graphic))
9822       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9823 
9824     return;
9825   }
9826 
9827   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9828     return;
9829 
9830   Feld[x][y] = EL_SP_EXIT_OPENING;
9831 
9832   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9833 }
9834 
CloseAllOpenTimegates()9835 static void CloseAllOpenTimegates()
9836 {
9837   int x, y;
9838 
9839   SCAN_PLAYFIELD(x, y)
9840   {
9841     int element = Feld[x][y];
9842 
9843     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9844     {
9845       Feld[x][y] = EL_TIMEGATE_CLOSING;
9846 
9847       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9848     }
9849   }
9850 }
9851 
DrawTwinkleOnField(int x,int y)9852 void DrawTwinkleOnField(int x, int y)
9853 {
9854   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9855     return;
9856 
9857   if (Feld[x][y] == EL_BD_DIAMOND)
9858     return;
9859 
9860   if (MovDelay[x][y] == 0)	/* next animation frame */
9861     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9862 
9863   if (MovDelay[x][y] != 0)	/* wait some time before next frame */
9864   {
9865     MovDelay[x][y]--;
9866 
9867     DrawLevelElementAnimation(x, y, Feld[x][y]);
9868 
9869     if (MovDelay[x][y] != 0)
9870     {
9871       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9872 					   10 - MovDelay[x][y]);
9873 
9874       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9875     }
9876   }
9877 }
9878 
MauerWaechst(int x,int y)9879 void MauerWaechst(int x, int y)
9880 {
9881   int delay = 6;
9882 
9883   if (!MovDelay[x][y])		/* next animation frame */
9884     MovDelay[x][y] = 3 * delay;
9885 
9886   if (MovDelay[x][y])		/* wait some time before next frame */
9887   {
9888     MovDelay[x][y]--;
9889 
9890     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9891     {
9892       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9893       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9894 
9895       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9896     }
9897 
9898     if (!MovDelay[x][y])
9899     {
9900       if (MovDir[x][y] == MV_LEFT)
9901       {
9902 	if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9903 	  TEST_DrawLevelField(x - 1, y);
9904       }
9905       else if (MovDir[x][y] == MV_RIGHT)
9906       {
9907 	if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9908 	  TEST_DrawLevelField(x + 1, y);
9909       }
9910       else if (MovDir[x][y] == MV_UP)
9911       {
9912 	if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9913 	  TEST_DrawLevelField(x, y - 1);
9914       }
9915       else
9916       {
9917 	if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9918 	  TEST_DrawLevelField(x, y + 1);
9919       }
9920 
9921       Feld[x][y] = Store[x][y];
9922       Store[x][y] = 0;
9923       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9924       TEST_DrawLevelField(x, y);
9925     }
9926   }
9927 }
9928 
MauerAbleger(int ax,int ay)9929 void MauerAbleger(int ax, int ay)
9930 {
9931   int element = Feld[ax][ay];
9932   int graphic = el2img(element);
9933   boolean oben_frei = FALSE, unten_frei = FALSE;
9934   boolean links_frei = FALSE, rechts_frei = FALSE;
9935   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9936   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9937   boolean new_wall = FALSE;
9938 
9939   if (IS_ANIMATED(graphic))
9940     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9941 
9942   if (!MovDelay[ax][ay])	/* start building new wall */
9943     MovDelay[ax][ay] = 6;
9944 
9945   if (MovDelay[ax][ay])		/* wait some time before building new wall */
9946   {
9947     MovDelay[ax][ay]--;
9948     if (MovDelay[ax][ay])
9949       return;
9950   }
9951 
9952   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9953     oben_frei = TRUE;
9954   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9955     unten_frei = TRUE;
9956   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9957     links_frei = TRUE;
9958   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9959     rechts_frei = TRUE;
9960 
9961   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9962       element == EL_EXPANDABLE_WALL_ANY)
9963   {
9964     if (oben_frei)
9965     {
9966       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9967       Store[ax][ay-1] = element;
9968       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9969       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9970   	DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9971 		    IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9972       new_wall = TRUE;
9973     }
9974     if (unten_frei)
9975     {
9976       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9977       Store[ax][ay+1] = element;
9978       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9979       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9980   	DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9981 		    IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9982       new_wall = TRUE;
9983     }
9984   }
9985 
9986   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9987       element == EL_EXPANDABLE_WALL_ANY ||
9988       element == EL_EXPANDABLE_WALL ||
9989       element == EL_BD_EXPANDABLE_WALL)
9990   {
9991     if (links_frei)
9992     {
9993       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9994       Store[ax-1][ay] = element;
9995       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9996       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9997   	DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9998 		    IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9999       new_wall = TRUE;
10000     }
10001 
10002     if (rechts_frei)
10003     {
10004       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10005       Store[ax+1][ay] = element;
10006       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10007       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10008   	DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10009 		    IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10010       new_wall = TRUE;
10011     }
10012   }
10013 
10014   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10015     TEST_DrawLevelField(ax, ay);
10016 
10017   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10018     oben_massiv = TRUE;
10019   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10020     unten_massiv = TRUE;
10021   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10022     links_massiv = TRUE;
10023   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10024     rechts_massiv = TRUE;
10025 
10026   if (((oben_massiv && unten_massiv) ||
10027        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10028        element == EL_EXPANDABLE_WALL) &&
10029       ((links_massiv && rechts_massiv) ||
10030        element == EL_EXPANDABLE_WALL_VERTICAL))
10031     Feld[ax][ay] = EL_WALL;
10032 
10033   if (new_wall)
10034     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10035 }
10036 
MauerAblegerStahl(int ax,int ay)10037 void MauerAblegerStahl(int ax, int ay)
10038 {
10039   int element = Feld[ax][ay];
10040   int graphic = el2img(element);
10041   boolean oben_frei = FALSE, unten_frei = FALSE;
10042   boolean links_frei = FALSE, rechts_frei = FALSE;
10043   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10044   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10045   boolean new_wall = FALSE;
10046 
10047   if (IS_ANIMATED(graphic))
10048     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10049 
10050   if (!MovDelay[ax][ay])	/* start building new wall */
10051     MovDelay[ax][ay] = 6;
10052 
10053   if (MovDelay[ax][ay])		/* wait some time before building new wall */
10054   {
10055     MovDelay[ax][ay]--;
10056     if (MovDelay[ax][ay])
10057       return;
10058   }
10059 
10060   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10061     oben_frei = TRUE;
10062   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10063     unten_frei = TRUE;
10064   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10065     links_frei = TRUE;
10066   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10067     rechts_frei = TRUE;
10068 
10069   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10070       element == EL_EXPANDABLE_STEELWALL_ANY)
10071   {
10072     if (oben_frei)
10073     {
10074       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10075       Store[ax][ay-1] = element;
10076       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10077       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10078   	DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10079 		    IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10080       new_wall = TRUE;
10081     }
10082     if (unten_frei)
10083     {
10084       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10085       Store[ax][ay+1] = element;
10086       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10087       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10088   	DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10089 		    IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10090       new_wall = TRUE;
10091     }
10092   }
10093 
10094   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10095       element == EL_EXPANDABLE_STEELWALL_ANY)
10096   {
10097     if (links_frei)
10098     {
10099       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10100       Store[ax-1][ay] = element;
10101       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10102       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10103   	DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10104 		    IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10105       new_wall = TRUE;
10106     }
10107 
10108     if (rechts_frei)
10109     {
10110       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10111       Store[ax+1][ay] = element;
10112       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10113       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10114   	DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10115 		    IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10116       new_wall = TRUE;
10117     }
10118   }
10119 
10120   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10121     oben_massiv = TRUE;
10122   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10123     unten_massiv = TRUE;
10124   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10125     links_massiv = TRUE;
10126   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10127     rechts_massiv = TRUE;
10128 
10129   if (((oben_massiv && unten_massiv) ||
10130        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10131       ((links_massiv && rechts_massiv) ||
10132        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10133     Feld[ax][ay] = EL_STEELWALL;
10134 
10135   if (new_wall)
10136     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10137 }
10138 
CheckForDragon(int x,int y)10139 void CheckForDragon(int x, int y)
10140 {
10141   int i, j;
10142   boolean dragon_found = FALSE;
10143   static int xy[4][2] =
10144   {
10145     { 0, -1 },
10146     { -1, 0 },
10147     { +1, 0 },
10148     { 0, +1 }
10149   };
10150 
10151   for (i = 0; i < NUM_DIRECTIONS; i++)
10152   {
10153     for (j = 0; j < 4; j++)
10154     {
10155       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10156 
10157       if (IN_LEV_FIELD(xx, yy) &&
10158 	  (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10159       {
10160 	if (Feld[xx][yy] == EL_DRAGON)
10161 	  dragon_found = TRUE;
10162       }
10163       else
10164 	break;
10165     }
10166   }
10167 
10168   if (!dragon_found)
10169   {
10170     for (i = 0; i < NUM_DIRECTIONS; i++)
10171     {
10172       for (j = 0; j < 3; j++)
10173       {
10174   	int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10175 
10176   	if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10177   	{
10178 	  Feld[xx][yy] = EL_EMPTY;
10179 	  TEST_DrawLevelField(xx, yy);
10180   	}
10181   	else
10182   	  break;
10183       }
10184     }
10185   }
10186 }
10187 
InitBuggyBase(int x,int y)10188 static void InitBuggyBase(int x, int y)
10189 {
10190   int element = Feld[x][y];
10191   int activating_delay = FRAMES_PER_SECOND / 4;
10192 
10193   ChangeDelay[x][y] =
10194     (element == EL_SP_BUGGY_BASE ?
10195      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10196      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10197      activating_delay :
10198      element == EL_SP_BUGGY_BASE_ACTIVE ?
10199      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10200 }
10201 
WarnBuggyBase(int x,int y)10202 static void WarnBuggyBase(int x, int y)
10203 {
10204   int i;
10205   static int xy[4][2] =
10206   {
10207     { 0, -1 },
10208     { -1, 0 },
10209     { +1, 0 },
10210     { 0, +1 }
10211   };
10212 
10213   for (i = 0; i < NUM_DIRECTIONS; i++)
10214   {
10215     int xx = x + xy[i][0];
10216     int yy = y + xy[i][1];
10217 
10218     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10219     {
10220       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10221 
10222       break;
10223     }
10224   }
10225 }
10226 
InitTrap(int x,int y)10227 static void InitTrap(int x, int y)
10228 {
10229   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10230 }
10231 
ActivateTrap(int x,int y)10232 static void ActivateTrap(int x, int y)
10233 {
10234   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10235 }
10236 
ChangeActiveTrap(int x,int y)10237 static void ChangeActiveTrap(int x, int y)
10238 {
10239   int graphic = IMG_TRAP_ACTIVE;
10240 
10241   /* if new animation frame was drawn, correct crumbled sand border */
10242   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10243     TEST_DrawLevelFieldCrumbled(x, y);
10244 }
10245 
getSpecialActionElement(int element,int number,int base_element)10246 static int getSpecialActionElement(int element, int number, int base_element)
10247 {
10248   return (element != EL_EMPTY ? element :
10249 	  number != -1 ? base_element + number - 1 :
10250 	  EL_EMPTY);
10251 }
10252 
getModifiedActionNumber(int value_old,int operator,int operand,int value_min,int value_max)10253 static int getModifiedActionNumber(int value_old, int operator, int operand,
10254 				   int value_min, int value_max)
10255 {
10256   int value_new = (operator == CA_MODE_SET      ? operand :
10257 		   operator == CA_MODE_ADD      ? value_old + operand :
10258 		   operator == CA_MODE_SUBTRACT ? value_old - operand :
10259 		   operator == CA_MODE_MULTIPLY ? value_old * operand :
10260 		   operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10261 		   operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10262 		   value_old);
10263 
10264   return (value_new < value_min ? value_min :
10265 	  value_new > value_max ? value_max :
10266 	  value_new);
10267 }
10268 
ExecuteCustomElementAction(int x,int y,int element,int page)10269 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10270 {
10271   struct ElementInfo *ei = &element_info[element];
10272   struct ElementChangeInfo *change = &ei->change_page[page];
10273   int target_element = change->target_element;
10274   int action_type = change->action_type;
10275   int action_mode = change->action_mode;
10276   int action_arg = change->action_arg;
10277   int action_element = change->action_element;
10278   int i;
10279 
10280   if (!change->has_action)
10281     return;
10282 
10283   /* ---------- determine action paramater values -------------------------- */
10284 
10285   int level_time_value =
10286     (level.time > 0 ? TimeLeft :
10287      TimePlayed);
10288 
10289   int action_arg_element_raw =
10290     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10291      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10292      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10293      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10294      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10295      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10296      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10297      EL_EMPTY);
10298   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10299 
10300 #if 0
10301   if (action_arg_element_raw == EL_GROUP_START)
10302     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10303 #endif
10304 
10305   int action_arg_direction =
10306     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10307      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10308      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10309      change->actual_trigger_side :
10310      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10311      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10312      MV_NONE);
10313 
10314   int action_arg_number_min =
10315     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10316      CA_ARG_MIN);
10317 
10318   int action_arg_number_max =
10319     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10320      action_type == CA_SET_LEVEL_GEMS ? 999 :
10321      action_type == CA_SET_LEVEL_TIME ? 9999 :
10322      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10323      action_type == CA_SET_CE_VALUE ? 9999 :
10324      action_type == CA_SET_CE_SCORE ? 9999 :
10325      CA_ARG_MAX);
10326 
10327   int action_arg_number_reset =
10328     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10329      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10330      action_type == CA_SET_LEVEL_TIME ? level.time :
10331      action_type == CA_SET_LEVEL_SCORE ? 0 :
10332 #if USE_NEW_CUSTOM_VALUE
10333      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10334 #else
10335      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10336 #endif
10337      action_type == CA_SET_CE_SCORE ? 0 :
10338      0);
10339 
10340   int action_arg_number =
10341     (action_arg <= CA_ARG_MAX ? action_arg :
10342      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10343      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10344      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10345      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10346      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10347      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10348 #if USE_NEW_CUSTOM_VALUE
10349      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10350 #else
10351      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10352 #endif
10353      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10354      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10355      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10356      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10357      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10358      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10359      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10360      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10361      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10362      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10363      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10364      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10365      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10366      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10367      -1);
10368 
10369   int action_arg_number_old =
10370     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10371      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10372      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10373      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10374      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10375      0);
10376 
10377   int action_arg_number_new =
10378     getModifiedActionNumber(action_arg_number_old,
10379 			    action_mode, action_arg_number,
10380 			    action_arg_number_min, action_arg_number_max);
10381 
10382 #if 1
10383   int trigger_player_bits =
10384     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10385      change->actual_trigger_player_bits : change->trigger_player);
10386 #else
10387   int trigger_player_bits =
10388     (change->actual_trigger_player >= EL_PLAYER_1 &&
10389      change->actual_trigger_player <= EL_PLAYER_4 ?
10390      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10391      PLAYER_BITS_ANY);
10392 #endif
10393 
10394   int action_arg_player_bits =
10395     (action_arg >= CA_ARG_PLAYER_1 &&
10396      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10397      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10398      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10399      PLAYER_BITS_ANY);
10400 
10401   /* ---------- execute action  -------------------------------------------- */
10402 
10403   switch (action_type)
10404   {
10405     case CA_NO_ACTION:
10406     {
10407       return;
10408     }
10409 
10410     /* ---------- level actions  ------------------------------------------- */
10411 
10412     case CA_RESTART_LEVEL:
10413     {
10414       game.restart_level = TRUE;
10415 
10416       break;
10417     }
10418 
10419     case CA_SHOW_ENVELOPE:
10420     {
10421       int element = getSpecialActionElement(action_arg_element,
10422 					    action_arg_number, EL_ENVELOPE_1);
10423 
10424       if (IS_ENVELOPE(element))
10425 	local_player->show_envelope = element;
10426 
10427       break;
10428     }
10429 
10430     case CA_SET_LEVEL_TIME:
10431     {
10432       if (level.time > 0)	/* only modify limited time value */
10433       {
10434 	TimeLeft = action_arg_number_new;
10435 
10436 #if 1
10437 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10438 
10439 	DisplayGameControlValues();
10440 #else
10441 	DrawGameValue_Time(TimeLeft);
10442 #endif
10443 
10444 	if (!TimeLeft && setup.time_limit)
10445 	  for (i = 0; i < MAX_PLAYERS; i++)
10446 	    KillPlayer(&stored_player[i]);
10447       }
10448 
10449       break;
10450     }
10451 
10452     case CA_SET_LEVEL_SCORE:
10453     {
10454       local_player->score = action_arg_number_new;
10455 
10456 #if 1
10457       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10458 
10459       DisplayGameControlValues();
10460 #else
10461       DrawGameValue_Score(local_player->score);
10462 #endif
10463 
10464       break;
10465     }
10466 
10467     case CA_SET_LEVEL_GEMS:
10468     {
10469       local_player->gems_still_needed = action_arg_number_new;
10470 
10471 #if 1
10472       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10473 
10474       DisplayGameControlValues();
10475 #else
10476       DrawGameValue_Emeralds(local_player->gems_still_needed);
10477 #endif
10478 
10479       break;
10480     }
10481 
10482 #if !USE_PLAYER_GRAVITY
10483     case CA_SET_LEVEL_GRAVITY:
10484     {
10485       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10486 		      action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10487 		      action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10488 		      game.gravity);
10489       break;
10490     }
10491 #endif
10492 
10493     case CA_SET_LEVEL_WIND:
10494     {
10495       game.wind_direction = action_arg_direction;
10496 
10497       break;
10498     }
10499 
10500     case CA_SET_LEVEL_RANDOM_SEED:
10501     {
10502 #if 1
10503       /* ensure that setting a new random seed while playing is predictable */
10504       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10505 #else
10506       InitRND(action_arg_number_new);
10507 #endif
10508 
10509 #if 0
10510       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10511 #endif
10512 
10513 #if 0
10514       {
10515 	int i;
10516 
10517 	printf("::: ");
10518 	for (i = 0; i < 9; i++)
10519 	  printf("%d, ", RND(2));
10520 	printf("\n");
10521       }
10522 #endif
10523 
10524       break;
10525     }
10526 
10527     /* ---------- player actions  ------------------------------------------ */
10528 
10529     case CA_MOVE_PLAYER:
10530     {
10531       /* automatically move to the next field in specified direction */
10532       for (i = 0; i < MAX_PLAYERS; i++)
10533 	if (trigger_player_bits & (1 << i))
10534 	  stored_player[i].programmed_action = action_arg_direction;
10535 
10536       break;
10537     }
10538 
10539     case CA_EXIT_PLAYER:
10540     {
10541       for (i = 0; i < MAX_PLAYERS; i++)
10542 	if (action_arg_player_bits & (1 << i))
10543 	  PlayerWins(&stored_player[i]);
10544 
10545       break;
10546     }
10547 
10548     case CA_KILL_PLAYER:
10549     {
10550       for (i = 0; i < MAX_PLAYERS; i++)
10551 	if (action_arg_player_bits & (1 << i))
10552 	  KillPlayer(&stored_player[i]);
10553 
10554       break;
10555     }
10556 
10557     case CA_SET_PLAYER_KEYS:
10558     {
10559       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10560       int element = getSpecialActionElement(action_arg_element,
10561 					    action_arg_number, EL_KEY_1);
10562 
10563       if (IS_KEY(element))
10564       {
10565 	for (i = 0; i < MAX_PLAYERS; i++)
10566 	{
10567 	  if (trigger_player_bits & (1 << i))
10568 	  {
10569 	    stored_player[i].key[KEY_NR(element)] = key_state;
10570 
10571 	    DrawGameDoorValues();
10572 	  }
10573 	}
10574       }
10575 
10576       break;
10577     }
10578 
10579     case CA_SET_PLAYER_SPEED:
10580     {
10581 #if 0
10582       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10583 #endif
10584 
10585       for (i = 0; i < MAX_PLAYERS; i++)
10586       {
10587 	if (trigger_player_bits & (1 << i))
10588 	{
10589 	  int move_stepsize = TILEX / stored_player[i].move_delay_value;
10590 
10591 	  if (action_arg == CA_ARG_SPEED_FASTER &&
10592 	      stored_player[i].cannot_move)
10593 	  {
10594 	    action_arg_number = STEPSIZE_VERY_SLOW;
10595 	  }
10596 	  else if (action_arg == CA_ARG_SPEED_SLOWER ||
10597 		   action_arg == CA_ARG_SPEED_FASTER)
10598 	  {
10599 	    action_arg_number = 2;
10600 	    action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10601 			   CA_MODE_MULTIPLY);
10602 	  }
10603 	  else if (action_arg == CA_ARG_NUMBER_RESET)
10604 	  {
10605 	    action_arg_number = level.initial_player_stepsize[i];
10606 	  }
10607 
10608 	  move_stepsize =
10609 	    getModifiedActionNumber(move_stepsize,
10610 				    action_mode,
10611 				    action_arg_number,
10612 				    action_arg_number_min,
10613 				    action_arg_number_max);
10614 
10615 	  SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10616 	}
10617       }
10618 
10619       break;
10620     }
10621 
10622     case CA_SET_PLAYER_SHIELD:
10623     {
10624       for (i = 0; i < MAX_PLAYERS; i++)
10625       {
10626 	if (trigger_player_bits & (1 << i))
10627 	{
10628 	  if (action_arg == CA_ARG_SHIELD_OFF)
10629 	  {
10630 	    stored_player[i].shield_normal_time_left = 0;
10631 	    stored_player[i].shield_deadly_time_left = 0;
10632 	  }
10633 	  else if (action_arg == CA_ARG_SHIELD_NORMAL)
10634 	  {
10635 	    stored_player[i].shield_normal_time_left = 999999;
10636 	  }
10637 	  else if (action_arg == CA_ARG_SHIELD_DEADLY)
10638 	  {
10639 	    stored_player[i].shield_normal_time_left = 999999;
10640 	    stored_player[i].shield_deadly_time_left = 999999;
10641 	  }
10642 	}
10643       }
10644 
10645       break;
10646     }
10647 
10648 #if USE_PLAYER_GRAVITY
10649     case CA_SET_PLAYER_GRAVITY:
10650     {
10651       for (i = 0; i < MAX_PLAYERS; i++)
10652       {
10653 	if (trigger_player_bits & (1 << i))
10654 	{
10655 	  stored_player[i].gravity =
10656 	    (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10657 	     action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10658 	     action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10659 	     stored_player[i].gravity);
10660 	}
10661       }
10662 
10663       break;
10664     }
10665 #endif
10666 
10667     case CA_SET_PLAYER_ARTWORK:
10668     {
10669       for (i = 0; i < MAX_PLAYERS; i++)
10670       {
10671 	if (trigger_player_bits & (1 << i))
10672 	{
10673 	  int artwork_element = action_arg_element;
10674 
10675 	  if (action_arg == CA_ARG_ELEMENT_RESET)
10676 	    artwork_element =
10677 	      (level.use_artwork_element[i] ? level.artwork_element[i] :
10678 	       stored_player[i].element_nr);
10679 
10680 #if USE_GFX_RESET_PLAYER_ARTWORK
10681 	  if (stored_player[i].artwork_element != artwork_element)
10682 	    stored_player[i].Frame = 0;
10683 #endif
10684 
10685 	  stored_player[i].artwork_element = artwork_element;
10686 
10687 	  SetPlayerWaiting(&stored_player[i], FALSE);
10688 
10689 	  /* set number of special actions for bored and sleeping animation */
10690 	  stored_player[i].num_special_action_bored =
10691 	    get_num_special_action(artwork_element,
10692 				   ACTION_BORING_1, ACTION_BORING_LAST);
10693 	  stored_player[i].num_special_action_sleeping =
10694 	    get_num_special_action(artwork_element,
10695 				   ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10696 	}
10697       }
10698 
10699       break;
10700     }
10701 
10702     case CA_SET_PLAYER_INVENTORY:
10703     {
10704       for (i = 0; i < MAX_PLAYERS; i++)
10705       {
10706 	struct PlayerInfo *player = &stored_player[i];
10707 	int j, k;
10708 
10709 	if (trigger_player_bits & (1 << i))
10710 	{
10711 	  int inventory_element = action_arg_element;
10712 
10713 	  if (action_arg == CA_ARG_ELEMENT_TARGET ||
10714 	      action_arg == CA_ARG_ELEMENT_TRIGGER ||
10715 	      action_arg == CA_ARG_ELEMENT_ACTION)
10716 	  {
10717 	    int element = inventory_element;
10718 	    int collect_count = element_info[element].collect_count_initial;
10719 
10720 	    if (!IS_CUSTOM_ELEMENT(element))
10721 	      collect_count = 1;
10722 
10723 	    if (collect_count == 0)
10724 	      player->inventory_infinite_element = element;
10725 	    else
10726 	      for (k = 0; k < collect_count; k++)
10727 		if (player->inventory_size < MAX_INVENTORY_SIZE)
10728 		  player->inventory_element[player->inventory_size++] =
10729 		    element;
10730 	  }
10731 	  else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10732 		   action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10733 		   action_arg == CA_ARG_INVENTORY_RM_ACTION)
10734 	  {
10735 	    if (player->inventory_infinite_element != EL_UNDEFINED &&
10736 		IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10737 				     action_arg_element_raw))
10738 	      player->inventory_infinite_element = EL_UNDEFINED;
10739 
10740 	    for (k = 0, j = 0; j < player->inventory_size; j++)
10741 	    {
10742 	      if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10743 					action_arg_element_raw))
10744 		player->inventory_element[k++] = player->inventory_element[j];
10745 	    }
10746 
10747 	    player->inventory_size = k;
10748 	  }
10749 	  else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10750 	  {
10751 	    if (player->inventory_size > 0)
10752 	    {
10753 	      for (j = 0; j < player->inventory_size - 1; j++)
10754 		player->inventory_element[j] = player->inventory_element[j + 1];
10755 
10756 	      player->inventory_size--;
10757 	    }
10758 	  }
10759 	  else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10760 	  {
10761 	    if (player->inventory_size > 0)
10762 	      player->inventory_size--;
10763 	  }
10764 	  else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10765 	  {
10766 	    player->inventory_infinite_element = EL_UNDEFINED;
10767 	    player->inventory_size = 0;
10768 	  }
10769 	  else if (action_arg == CA_ARG_INVENTORY_RESET)
10770 	  {
10771 	    player->inventory_infinite_element = EL_UNDEFINED;
10772 	    player->inventory_size = 0;
10773 
10774 	    if (level.use_initial_inventory[i])
10775 	    {
10776 	      for (j = 0; j < level.initial_inventory_size[i]; j++)
10777 	      {
10778 		int element = level.initial_inventory_content[i][j];
10779 		int collect_count = element_info[element].collect_count_initial;
10780 
10781 		if (!IS_CUSTOM_ELEMENT(element))
10782 		  collect_count = 1;
10783 
10784 		if (collect_count == 0)
10785 		  player->inventory_infinite_element = element;
10786 		else
10787 		  for (k = 0; k < collect_count; k++)
10788 		    if (player->inventory_size < MAX_INVENTORY_SIZE)
10789 		      player->inventory_element[player->inventory_size++] =
10790 			element;
10791 	      }
10792 	    }
10793 	  }
10794 	}
10795       }
10796 
10797       break;
10798     }
10799 
10800     /* ---------- CE actions  ---------------------------------------------- */
10801 
10802     case CA_SET_CE_VALUE:
10803     {
10804 #if USE_NEW_CUSTOM_VALUE
10805       int last_ce_value = CustomValue[x][y];
10806 
10807       CustomValue[x][y] = action_arg_number_new;
10808 
10809       if (CustomValue[x][y] != last_ce_value)
10810       {
10811 	CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10812 	CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10813 
10814 	if (CustomValue[x][y] == 0)
10815 	{
10816 	  CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10817 	  CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10818 	}
10819       }
10820 #endif
10821 
10822       break;
10823     }
10824 
10825     case CA_SET_CE_SCORE:
10826     {
10827 #if USE_NEW_CUSTOM_VALUE
10828       int last_ce_score = ei->collect_score;
10829 
10830       ei->collect_score = action_arg_number_new;
10831 
10832       if (ei->collect_score != last_ce_score)
10833       {
10834 	CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10835 	CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10836 
10837 	if (ei->collect_score == 0)
10838 	{
10839 	  int xx, yy;
10840 
10841 	  CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10842 	  CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10843 
10844 	  /*
10845 	    This is a very special case that seems to be a mixture between
10846 	    CheckElementChange() and CheckTriggeredElementChange(): while
10847 	    the first one only affects single elements that are triggered
10848 	    directly, the second one affects multiple elements in the playfield
10849 	    that are triggered indirectly by another element. This is a third
10850 	    case: Changing the CE score always affects multiple identical CEs,
10851 	    so every affected CE must be checked, not only the single CE for
10852 	    which the CE score was changed in the first place (as every instance
10853 	    of that CE shares the same CE score, and therefore also can change)!
10854 	  */
10855 	  SCAN_PLAYFIELD(xx, yy)
10856 	  {
10857 	    if (Feld[xx][yy] == element)
10858 	      CheckElementChange(xx, yy, element, EL_UNDEFINED,
10859 				 CE_SCORE_GETS_ZERO);
10860 	  }
10861 	}
10862       }
10863 #endif
10864 
10865       break;
10866     }
10867 
10868     case CA_SET_CE_ARTWORK:
10869     {
10870       int artwork_element = action_arg_element;
10871       boolean reset_frame = FALSE;
10872       int xx, yy;
10873 
10874       if (action_arg == CA_ARG_ELEMENT_RESET)
10875 	artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10876 			   element);
10877 
10878       if (ei->gfx_element != artwork_element)
10879 	reset_frame = TRUE;
10880 
10881       ei->gfx_element = artwork_element;
10882 
10883       SCAN_PLAYFIELD(xx, yy)
10884       {
10885 	if (Feld[xx][yy] == element)
10886 	{
10887 	  if (reset_frame)
10888 	  {
10889 	    ResetGfxAnimation(xx, yy);
10890 	    ResetRandomAnimationValue(xx, yy);
10891 	  }
10892 
10893 	  TEST_DrawLevelField(xx, yy);
10894 	}
10895       }
10896 
10897       break;
10898     }
10899 
10900     /* ---------- engine actions  ------------------------------------------ */
10901 
10902     case CA_SET_ENGINE_SCAN_MODE:
10903     {
10904       InitPlayfieldScanMode(action_arg);
10905 
10906       break;
10907     }
10908 
10909     default:
10910       break;
10911   }
10912 }
10913 
CreateFieldExt(int x,int y,int element,boolean is_change)10914 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10915 {
10916   int old_element = Feld[x][y];
10917   int new_element = GetElementFromGroupElement(element);
10918   int previous_move_direction = MovDir[x][y];
10919 #if USE_NEW_CUSTOM_VALUE
10920   int last_ce_value = CustomValue[x][y];
10921 #endif
10922   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10923   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10924   boolean add_player_onto_element = (new_element_is_player &&
10925 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
10926 				     /* this breaks SnakeBite when a snake is
10927 					halfway through a door that closes */
10928 				     /* NOW FIXED AT LEVEL INIT IN files.c */
10929 				     new_element != EL_SOKOBAN_FIELD_PLAYER &&
10930 #endif
10931 				     IS_WALKABLE(old_element));
10932 
10933 #if 0
10934   /* check if element under the player changes from accessible to unaccessible
10935      (needed for special case of dropping element which then changes) */
10936   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10937       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10938   {
10939     Bang(x, y);
10940 
10941     return;
10942   }
10943 #endif
10944 
10945   if (!add_player_onto_element)
10946   {
10947     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10948       RemoveMovingField(x, y);
10949     else
10950       RemoveField(x, y);
10951 
10952     Feld[x][y] = new_element;
10953 
10954 #if !USE_GFX_RESET_GFX_ANIMATION
10955     ResetGfxAnimation(x, y);
10956     ResetRandomAnimationValue(x, y);
10957 #endif
10958 
10959     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10960       MovDir[x][y] = previous_move_direction;
10961 
10962 #if USE_NEW_CUSTOM_VALUE
10963     if (element_info[new_element].use_last_ce_value)
10964       CustomValue[x][y] = last_ce_value;
10965 #endif
10966 
10967     InitField_WithBug1(x, y, FALSE);
10968 
10969     new_element = Feld[x][y];	/* element may have changed */
10970 
10971 #if USE_GFX_RESET_GFX_ANIMATION
10972     ResetGfxAnimation(x, y);
10973     ResetRandomAnimationValue(x, y);
10974 #endif
10975 
10976     TEST_DrawLevelField(x, y);
10977 
10978     if (GFX_CRUMBLED(new_element))
10979       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10980   }
10981 
10982 #if 1
10983   /* check if element under the player changes from accessible to unaccessible
10984      (needed for special case of dropping element which then changes) */
10985   /* (must be checked after creating new element for walkable group elements) */
10986 #if USE_FIX_KILLED_BY_NON_WALKABLE
10987   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10988       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10989   {
10990     Bang(x, y);
10991 
10992     return;
10993   }
10994 #else
10995   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
10996       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10997   {
10998     Bang(x, y);
10999 
11000     return;
11001   }
11002 #endif
11003 #endif
11004 
11005   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11006   if (new_element_is_player)
11007     RelocatePlayer(x, y, new_element);
11008 
11009   if (is_change)
11010     ChangeCount[x][y]++;	/* count number of changes in the same frame */
11011 
11012   TestIfBadThingTouchesPlayer(x, y);
11013   TestIfPlayerTouchesCustomElement(x, y);
11014   TestIfElementTouchesCustomElement(x, y);
11015 }
11016 
CreateField(int x,int y,int element)11017 static void CreateField(int x, int y, int element)
11018 {
11019   CreateFieldExt(x, y, element, FALSE);
11020 }
11021 
CreateElementFromChange(int x,int y,int element)11022 static void CreateElementFromChange(int x, int y, int element)
11023 {
11024   element = GET_VALID_RUNTIME_ELEMENT(element);
11025 
11026 #if USE_STOP_CHANGED_ELEMENTS
11027   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11028   {
11029     int old_element = Feld[x][y];
11030 
11031     /* prevent changed element from moving in same engine frame
11032        unless both old and new element can either fall or move */
11033     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11034 	(!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11035       Stop[x][y] = TRUE;
11036   }
11037 #endif
11038 
11039   CreateFieldExt(x, y, element, TRUE);
11040 }
11041 
ChangeElement(int x,int y,int element,int page)11042 static boolean ChangeElement(int x, int y, int element, int page)
11043 {
11044   struct ElementInfo *ei = &element_info[element];
11045   struct ElementChangeInfo *change = &ei->change_page[page];
11046   int ce_value = CustomValue[x][y];
11047   int ce_score = ei->collect_score;
11048   int target_element;
11049   int old_element = Feld[x][y];
11050 
11051   /* always use default change event to prevent running into a loop */
11052   if (ChangeEvent[x][y] == -1)
11053     ChangeEvent[x][y] = CE_DELAY;
11054 
11055   if (ChangeEvent[x][y] == CE_DELAY)
11056   {
11057     /* reset actual trigger element, trigger player and action element */
11058     change->actual_trigger_element = EL_EMPTY;
11059     change->actual_trigger_player = EL_EMPTY;
11060     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11061     change->actual_trigger_side = CH_SIDE_NONE;
11062     change->actual_trigger_ce_value = 0;
11063     change->actual_trigger_ce_score = 0;
11064   }
11065 
11066   /* do not change elements more than a specified maximum number of changes */
11067   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11068     return FALSE;
11069 
11070   ChangeCount[x][y]++;		/* count number of changes in the same frame */
11071 
11072   if (change->explode)
11073   {
11074     Bang(x, y);
11075 
11076     return TRUE;
11077   }
11078 
11079   if (change->use_target_content)
11080   {
11081     boolean complete_replace = TRUE;
11082     boolean can_replace[3][3];
11083     int xx, yy;
11084 
11085     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11086     {
11087       boolean is_empty;
11088       boolean is_walkable;
11089       boolean is_diggable;
11090       boolean is_collectible;
11091       boolean is_removable;
11092       boolean is_destructible;
11093       int ex = x + xx - 1;
11094       int ey = y + yy - 1;
11095       int content_element = change->target_content.e[xx][yy];
11096       int e;
11097 
11098       can_replace[xx][yy] = TRUE;
11099 
11100       if (ex == x && ey == y)	/* do not check changing element itself */
11101 	continue;
11102 
11103       if (content_element == EL_EMPTY_SPACE)
11104       {
11105 	can_replace[xx][yy] = FALSE;	/* do not replace border with space */
11106 
11107 	continue;
11108       }
11109 
11110       if (!IN_LEV_FIELD(ex, ey))
11111       {
11112 	can_replace[xx][yy] = FALSE;
11113 	complete_replace = FALSE;
11114 
11115 	continue;
11116       }
11117 
11118       e = Feld[ex][ey];
11119 
11120       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11121 	e = MovingOrBlocked2Element(ex, ey);
11122 
11123       is_empty = (IS_FREE(ex, ey) ||
11124 		  (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11125 
11126       is_walkable     = (is_empty || IS_WALKABLE(e));
11127       is_diggable     = (is_empty || IS_DIGGABLE(e));
11128       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11129       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11130       is_removable    = (is_diggable || is_collectible);
11131 
11132       can_replace[xx][yy] =
11133 	(((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11134 	  (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11135 	  (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11136 	  (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11137 	  (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11138 	  (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11139 	 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11140 
11141       if (!can_replace[xx][yy])
11142 	complete_replace = FALSE;
11143     }
11144 
11145     if (!change->only_if_complete || complete_replace)
11146     {
11147       boolean something_has_changed = FALSE;
11148 
11149       if (change->only_if_complete && change->use_random_replace &&
11150 	  RND(100) < change->random_percentage)
11151 	return FALSE;
11152 
11153       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11154       {
11155 	int ex = x + xx - 1;
11156 	int ey = y + yy - 1;
11157 	int content_element;
11158 
11159 	if (can_replace[xx][yy] && (!change->use_random_replace ||
11160 				    RND(100) < change->random_percentage))
11161 	{
11162 	  if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11163 	    RemoveMovingField(ex, ey);
11164 
11165 	  ChangeEvent[ex][ey] = ChangeEvent[x][y];
11166 
11167 	  content_element = change->target_content.e[xx][yy];
11168 	  target_element = GET_TARGET_ELEMENT(element, content_element, change,
11169 					      ce_value, ce_score);
11170 
11171 	  CreateElementFromChange(ex, ey, target_element);
11172 
11173 	  something_has_changed = TRUE;
11174 
11175 	  /* for symmetry reasons, freeze newly created border elements */
11176 	  if (ex != x || ey != y)
11177 	    Stop[ex][ey] = TRUE;	/* no more moving in this frame */
11178 	}
11179       }
11180 
11181       if (something_has_changed)
11182       {
11183 	PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11184 	PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11185       }
11186     }
11187   }
11188   else
11189   {
11190     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11191 					ce_value, ce_score);
11192 
11193     if (element == EL_DIAGONAL_GROWING ||
11194 	element == EL_DIAGONAL_SHRINKING)
11195     {
11196       target_element = Store[x][y];
11197 
11198       Store[x][y] = EL_EMPTY;
11199     }
11200 
11201     CreateElementFromChange(x, y, target_element);
11202 
11203     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11204     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11205   }
11206 
11207   /* this uses direct change before indirect change */
11208   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11209 
11210   return TRUE;
11211 }
11212 
11213 #if USE_NEW_DELAYED_ACTION
11214 
HandleElementChange(int x,int y,int page)11215 static void HandleElementChange(int x, int y, int page)
11216 {
11217   int element = MovingOrBlocked2Element(x, y);
11218   struct ElementInfo *ei = &element_info[element];
11219   struct ElementChangeInfo *change = &ei->change_page[page];
11220   boolean handle_action_before_change = FALSE;
11221 
11222 #ifdef DEBUG
11223   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11224       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11225   {
11226     printf("\n\n");
11227     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11228 	   x, y, element, element_info[element].token_name);
11229     printf("HandleElementChange(): This should never happen!\n");
11230     printf("\n\n");
11231   }
11232 #endif
11233 
11234   /* this can happen with classic bombs on walkable, changing elements */
11235   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11236   {
11237 #if 0
11238     if (!CAN_CHANGE(Back[x][y]))	/* prevent permanent repetition */
11239       ChangeDelay[x][y] = 0;
11240 #endif
11241 
11242     return;
11243   }
11244 
11245   if (ChangeDelay[x][y] == 0)		/* initialize element change */
11246   {
11247     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11248 
11249     if (change->can_change)
11250     {
11251 #if 1
11252       /* !!! not clear why graphic animation should be reset at all here !!! */
11253       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11254 #if USE_GFX_RESET_WHEN_NOT_MOVING
11255       /* when a custom element is about to change (for example by change delay),
11256 	 do not reset graphic animation when the custom element is moving */
11257       if (!IS_MOVING(x, y))
11258 #endif
11259       {
11260 	ResetGfxAnimation(x, y);
11261 	ResetRandomAnimationValue(x, y);
11262       }
11263 #endif
11264 
11265       if (change->pre_change_function)
11266 	change->pre_change_function(x, y);
11267     }
11268   }
11269 
11270   ChangeDelay[x][y]--;
11271 
11272   if (ChangeDelay[x][y] != 0)		/* continue element change */
11273   {
11274     if (change->can_change)
11275     {
11276       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11277 
11278       if (IS_ANIMATED(graphic))
11279 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11280 
11281       if (change->change_function)
11282 	change->change_function(x, y);
11283     }
11284   }
11285   else					/* finish element change */
11286   {
11287     if (ChangePage[x][y] != -1)		/* remember page from delayed change */
11288     {
11289       page = ChangePage[x][y];
11290       ChangePage[x][y] = -1;
11291 
11292       change = &ei->change_page[page];
11293     }
11294 
11295     if (IS_MOVING(x, y))		/* never change a running system ;-) */
11296     {
11297       ChangeDelay[x][y] = 1;		/* try change after next move step */
11298       ChangePage[x][y] = page;		/* remember page to use for change */
11299 
11300       return;
11301     }
11302 
11303 #if 1
11304     /* special case: set new level random seed before changing element */
11305     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11306       handle_action_before_change = TRUE;
11307 
11308     if (change->has_action && handle_action_before_change)
11309       ExecuteCustomElementAction(x, y, element, page);
11310 #endif
11311 
11312     if (change->can_change)
11313     {
11314       if (ChangeElement(x, y, element, page))
11315       {
11316 	if (change->post_change_function)
11317 	  change->post_change_function(x, y);
11318       }
11319     }
11320 
11321     if (change->has_action && !handle_action_before_change)
11322       ExecuteCustomElementAction(x, y, element, page);
11323   }
11324 }
11325 
11326 #else
11327 
HandleElementChange(int x,int y,int page)11328 static void HandleElementChange(int x, int y, int page)
11329 {
11330   int element = MovingOrBlocked2Element(x, y);
11331   struct ElementInfo *ei = &element_info[element];
11332   struct ElementChangeInfo *change = &ei->change_page[page];
11333 
11334 #ifdef DEBUG
11335   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11336   {
11337     printf("\n\n");
11338     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11339 	   x, y, element, element_info[element].token_name);
11340     printf("HandleElementChange(): This should never happen!\n");
11341     printf("\n\n");
11342   }
11343 #endif
11344 
11345   /* this can happen with classic bombs on walkable, changing elements */
11346   if (!CAN_CHANGE(element))
11347   {
11348 #if 0
11349     if (!CAN_CHANGE(Back[x][y]))	/* prevent permanent repetition */
11350       ChangeDelay[x][y] = 0;
11351 #endif
11352 
11353     return;
11354   }
11355 
11356   if (ChangeDelay[x][y] == 0)		/* initialize element change */
11357   {
11358     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11359 
11360     ResetGfxAnimation(x, y);
11361     ResetRandomAnimationValue(x, y);
11362 
11363     if (change->pre_change_function)
11364       change->pre_change_function(x, y);
11365   }
11366 
11367   ChangeDelay[x][y]--;
11368 
11369   if (ChangeDelay[x][y] != 0)		/* continue element change */
11370   {
11371     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11372 
11373     if (IS_ANIMATED(graphic))
11374       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11375 
11376     if (change->change_function)
11377       change->change_function(x, y);
11378   }
11379   else					/* finish element change */
11380   {
11381     if (ChangePage[x][y] != -1)		/* remember page from delayed change */
11382     {
11383       page = ChangePage[x][y];
11384       ChangePage[x][y] = -1;
11385 
11386       change = &ei->change_page[page];
11387     }
11388 
11389     if (IS_MOVING(x, y))		/* never change a running system ;-) */
11390     {
11391       ChangeDelay[x][y] = 1;		/* try change after next move step */
11392       ChangePage[x][y] = page;		/* remember page to use for change */
11393 
11394       return;
11395     }
11396 
11397     if (ChangeElement(x, y, element, page))
11398     {
11399       if (change->post_change_function)
11400 	change->post_change_function(x, y);
11401     }
11402   }
11403 }
11404 
11405 #endif
11406 
CheckTriggeredElementChangeExt(int trigger_x,int trigger_y,int trigger_element,int trigger_event,int trigger_player,int trigger_side,int trigger_page)11407 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11408 					      int trigger_element,
11409 					      int trigger_event,
11410 					      int trigger_player,
11411 					      int trigger_side,
11412 					      int trigger_page)
11413 {
11414   boolean change_done_any = FALSE;
11415   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11416   int i;
11417 
11418   if (!(trigger_events[trigger_element][trigger_event]))
11419     return FALSE;
11420 
11421 #if 0
11422   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11423 	 trigger_event, recursion_loop_depth, recursion_loop_detected,
11424 	 recursion_loop_element, EL_NAME(recursion_loop_element));
11425 #endif
11426 
11427   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11428 
11429   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11430   {
11431     int element = EL_CUSTOM_START + i;
11432     boolean change_done = FALSE;
11433     int p;
11434 
11435     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11436 	!HAS_ANY_CHANGE_EVENT(element, trigger_event))
11437       continue;
11438 
11439     for (p = 0; p < element_info[element].num_change_pages; p++)
11440     {
11441       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11442 
11443       if (change->can_change_or_has_action &&
11444 	  change->has_event[trigger_event] &&
11445 	  change->trigger_side & trigger_side &&
11446 	  change->trigger_player & trigger_player &&
11447 	  change->trigger_page & trigger_page_bits &&
11448 	  IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11449       {
11450 	change->actual_trigger_element = trigger_element;
11451 	change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11452 	change->actual_trigger_player_bits = trigger_player;
11453 	change->actual_trigger_side = trigger_side;
11454 	change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11455 	change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11456 
11457 #if 0
11458 	printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11459 	       element, EL_NAME(element), p);
11460 #endif
11461 
11462 	if ((change->can_change && !change_done) || change->has_action)
11463 	{
11464 	  int x, y;
11465 
11466 	  SCAN_PLAYFIELD(x, y)
11467 	  {
11468 	    if (Feld[x][y] == element)
11469 	    {
11470 	      if (change->can_change && !change_done)
11471 	      {
11472 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11473 		/* if element already changed in this frame, not only prevent
11474 		   another element change (checked in ChangeElement()), but
11475 		   also prevent additional element actions for this element */
11476 
11477 		if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11478 		    !level.use_action_after_change_bug)
11479 		  continue;
11480 #endif
11481 
11482 #if 0
11483 		printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11484 		       element, EL_NAME(element), p);
11485 #endif
11486 
11487 		ChangeDelay[x][y] = 1;
11488 		ChangeEvent[x][y] = trigger_event;
11489 
11490 		HandleElementChange(x, y, p);
11491 	      }
11492 #if USE_NEW_DELAYED_ACTION
11493 	      else if (change->has_action)
11494 	      {
11495 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11496 		/* if element already changed in this frame, not only prevent
11497 		   another element change (checked in ChangeElement()), but
11498 		   also prevent additional element actions for this element */
11499 
11500 		if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11501 		    !level.use_action_after_change_bug)
11502 		  continue;
11503 #endif
11504 
11505 
11506 #if 0
11507 		printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11508 		       element, EL_NAME(element), p);
11509 #endif
11510 
11511 		ExecuteCustomElementAction(x, y, element, p);
11512 		PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11513 	      }
11514 #else
11515 	      if (change->has_action)
11516 	      {
11517 		ExecuteCustomElementAction(x, y, element, p);
11518 		PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11519 	      }
11520 #endif
11521 	    }
11522 	  }
11523 
11524 	  if (change->can_change)
11525 	  {
11526 	    change_done = TRUE;
11527 	    change_done_any = TRUE;
11528 
11529 #if 0
11530 	    printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11531 		   element, EL_NAME(element), p);
11532 #endif
11533 
11534 	  }
11535 	}
11536       }
11537     }
11538   }
11539 
11540   RECURSION_LOOP_DETECTION_END();
11541 
11542   return change_done_any;
11543 }
11544 
CheckElementChangeExt(int x,int y,int element,int trigger_element,int trigger_event,int trigger_player,int trigger_side)11545 static boolean CheckElementChangeExt(int x, int y,
11546 				     int element,
11547 				     int trigger_element,
11548 				     int trigger_event,
11549 				     int trigger_player,
11550 				     int trigger_side)
11551 {
11552   boolean change_done = FALSE;
11553   int p;
11554 
11555   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11556       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11557     return FALSE;
11558 
11559   if (Feld[x][y] == EL_BLOCKED)
11560   {
11561     Blocked2Moving(x, y, &x, &y);
11562     element = Feld[x][y];
11563   }
11564 
11565 #if 0
11566   /* check if element has already changed */
11567   if (Feld[x][y] != element)
11568     return FALSE;
11569 #else
11570   /* check if element has already changed or is about to change after moving */
11571   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11572        Feld[x][y] != element) ||
11573 
11574       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11575        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11576 	ChangePage[x][y] != -1)))
11577     return FALSE;
11578 #endif
11579 
11580 #if 0
11581   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11582 	 trigger_event, recursion_loop_depth, recursion_loop_detected,
11583 	 recursion_loop_element, EL_NAME(recursion_loop_element));
11584 #endif
11585 
11586   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11587 
11588 #if 0
11589   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11590 #endif
11591 
11592   for (p = 0; p < element_info[element].num_change_pages; p++)
11593   {
11594     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11595 
11596     /* check trigger element for all events where the element that is checked
11597        for changing interacts with a directly adjacent element -- this is
11598        different to element changes that affect other elements to change on the
11599        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11600     boolean check_trigger_element =
11601       (trigger_event == CE_TOUCHING_X ||
11602        trigger_event == CE_HITTING_X ||
11603        trigger_event == CE_HIT_BY_X ||
11604 #if 1
11605        /* this one was forgotten until 3.2.3 */
11606        trigger_event == CE_DIGGING_X);
11607 #endif
11608 
11609     if (change->can_change_or_has_action &&
11610 	change->has_event[trigger_event] &&
11611 	change->trigger_side & trigger_side &&
11612 	change->trigger_player & trigger_player &&
11613 	(!check_trigger_element ||
11614 	 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11615     {
11616       change->actual_trigger_element = trigger_element;
11617       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11618       change->actual_trigger_player_bits = trigger_player;
11619       change->actual_trigger_side = trigger_side;
11620       change->actual_trigger_ce_value = CustomValue[x][y];
11621       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11622 
11623       /* special case: trigger element not at (x,y) position for some events */
11624       if (check_trigger_element)
11625       {
11626 	static struct
11627 	{
11628 	  int dx, dy;
11629 	} move_xy[] =
11630 	  {
11631 	    {  0,  0 },
11632 	    { -1,  0 },
11633 	    { +1,  0 },
11634 	    {  0,  0 },
11635 	    {  0, -1 },
11636 	    {  0,  0 }, { 0, 0 }, { 0, 0 },
11637 	    {  0, +1 }
11638 	  };
11639 
11640 	int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11641 	int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11642 
11643 	change->actual_trigger_ce_value = CustomValue[xx][yy];
11644 	change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11645       }
11646 
11647       if (change->can_change && !change_done)
11648       {
11649 	ChangeDelay[x][y] = 1;
11650 	ChangeEvent[x][y] = trigger_event;
11651 
11652 	HandleElementChange(x, y, p);
11653 
11654 	change_done = TRUE;
11655       }
11656 #if USE_NEW_DELAYED_ACTION
11657       else if (change->has_action)
11658       {
11659 	ExecuteCustomElementAction(x, y, element, p);
11660 	PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11661       }
11662 #else
11663       if (change->has_action)
11664       {
11665 	ExecuteCustomElementAction(x, y, element, p);
11666 	PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11667       }
11668 #endif
11669     }
11670   }
11671 
11672   RECURSION_LOOP_DETECTION_END();
11673 
11674   return change_done;
11675 }
11676 
PlayPlayerSound(struct PlayerInfo * player)11677 static void PlayPlayerSound(struct PlayerInfo *player)
11678 {
11679   int jx = player->jx, jy = player->jy;
11680   int sound_element = player->artwork_element;
11681   int last_action = player->last_action_waiting;
11682   int action = player->action_waiting;
11683 
11684   if (player->is_waiting)
11685   {
11686     if (action != last_action)
11687       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11688     else
11689       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11690   }
11691   else
11692   {
11693     if (action != last_action)
11694       StopSound(element_info[sound_element].sound[last_action]);
11695 
11696     if (last_action == ACTION_SLEEPING)
11697       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11698   }
11699 }
11700 
PlayAllPlayersSound()11701 static void PlayAllPlayersSound()
11702 {
11703   int i;
11704 
11705   for (i = 0; i < MAX_PLAYERS; i++)
11706     if (stored_player[i].active)
11707       PlayPlayerSound(&stored_player[i]);
11708 }
11709 
SetPlayerWaiting(struct PlayerInfo * player,boolean is_waiting)11710 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11711 {
11712   boolean last_waiting = player->is_waiting;
11713   int move_dir = player->MovDir;
11714 
11715   player->dir_waiting = move_dir;
11716   player->last_action_waiting = player->action_waiting;
11717 
11718   if (is_waiting)
11719   {
11720     if (!last_waiting)		/* not waiting -> waiting */
11721     {
11722       player->is_waiting = TRUE;
11723 
11724       player->frame_counter_bored =
11725 	FrameCounter +
11726 	game.player_boring_delay_fixed +
11727 	GetSimpleRandom(game.player_boring_delay_random);
11728       player->frame_counter_sleeping =
11729 	FrameCounter +
11730 	game.player_sleeping_delay_fixed +
11731 	GetSimpleRandom(game.player_sleeping_delay_random);
11732 
11733       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11734     }
11735 
11736     if (game.player_sleeping_delay_fixed +
11737 	game.player_sleeping_delay_random > 0 &&
11738 	player->anim_delay_counter == 0 &&
11739 	player->post_delay_counter == 0 &&
11740 	FrameCounter >= player->frame_counter_sleeping)
11741       player->is_sleeping = TRUE;
11742     else if (game.player_boring_delay_fixed +
11743 	     game.player_boring_delay_random > 0 &&
11744 	     FrameCounter >= player->frame_counter_bored)
11745       player->is_bored = TRUE;
11746 
11747     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11748 			      player->is_bored ? ACTION_BORING :
11749 			      ACTION_WAITING);
11750 
11751     if (player->is_sleeping && player->use_murphy)
11752     {
11753       /* special case for sleeping Murphy when leaning against non-free tile */
11754 
11755       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11756 	  (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11757 	   !IS_MOVING(player->jx - 1, player->jy)))
11758 	move_dir = MV_LEFT;
11759       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11760 	       (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11761 		!IS_MOVING(player->jx + 1, player->jy)))
11762 	move_dir = MV_RIGHT;
11763       else
11764 	player->is_sleeping = FALSE;
11765 
11766       player->dir_waiting = move_dir;
11767     }
11768 
11769     if (player->is_sleeping)
11770     {
11771       if (player->num_special_action_sleeping > 0)
11772       {
11773 	if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11774 	{
11775 	  int last_special_action = player->special_action_sleeping;
11776 	  int num_special_action = player->num_special_action_sleeping;
11777 	  int special_action =
11778 	    (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11779 	     last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11780 	     last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11781 	     last_special_action + 1 : ACTION_SLEEPING);
11782 	  int special_graphic =
11783 	    el_act_dir2img(player->artwork_element, special_action, move_dir);
11784 
11785 	  player->anim_delay_counter =
11786 	    graphic_info[special_graphic].anim_delay_fixed +
11787 	    GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11788 	  player->post_delay_counter =
11789 	    graphic_info[special_graphic].post_delay_fixed +
11790 	    GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11791 
11792 	  player->special_action_sleeping = special_action;
11793 	}
11794 
11795 	if (player->anim_delay_counter > 0)
11796 	{
11797 	  player->action_waiting = player->special_action_sleeping;
11798 	  player->anim_delay_counter--;
11799 	}
11800 	else if (player->post_delay_counter > 0)
11801 	{
11802 	  player->post_delay_counter--;
11803 	}
11804       }
11805     }
11806     else if (player->is_bored)
11807     {
11808       if (player->num_special_action_bored > 0)
11809       {
11810 	if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11811 	{
11812 	  int special_action =
11813 	    ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11814 	  int special_graphic =
11815 	    el_act_dir2img(player->artwork_element, special_action, move_dir);
11816 
11817 	  player->anim_delay_counter =
11818 	    graphic_info[special_graphic].anim_delay_fixed +
11819 	    GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11820 	  player->post_delay_counter =
11821 	    graphic_info[special_graphic].post_delay_fixed +
11822 	    GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11823 
11824 	  player->special_action_bored = special_action;
11825 	}
11826 
11827 	if (player->anim_delay_counter > 0)
11828 	{
11829 	  player->action_waiting = player->special_action_bored;
11830 	  player->anim_delay_counter--;
11831 	}
11832 	else if (player->post_delay_counter > 0)
11833 	{
11834 	  player->post_delay_counter--;
11835 	}
11836       }
11837     }
11838   }
11839   else if (last_waiting)	/* waiting -> not waiting */
11840   {
11841     player->is_waiting = FALSE;
11842     player->is_bored = FALSE;
11843     player->is_sleeping = FALSE;
11844 
11845     player->frame_counter_bored = -1;
11846     player->frame_counter_sleeping = -1;
11847 
11848     player->anim_delay_counter = 0;
11849     player->post_delay_counter = 0;
11850 
11851     player->dir_waiting = player->MovDir;
11852     player->action_waiting = ACTION_DEFAULT;
11853 
11854     player->special_action_bored = ACTION_DEFAULT;
11855     player->special_action_sleeping = ACTION_DEFAULT;
11856   }
11857 }
11858 
CheckSingleStepMode(struct PlayerInfo * player)11859 static void CheckSingleStepMode(struct PlayerInfo *player)
11860 {
11861   if (tape.single_step && tape.recording && !tape.pausing)
11862   {
11863     /* as it is called "single step mode", just return to pause mode when the
11864        player stopped moving after one tile (or never starts moving at all) */
11865     if (!player->is_moving && !player->is_pushing)
11866     {
11867       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11868       SnapField(player, 0, 0);			/* stop snapping */
11869     }
11870   }
11871 }
11872 
PlayerActions(struct PlayerInfo * player,byte player_action)11873 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11874 {
11875   int left	= player_action & JOY_LEFT;
11876   int right	= player_action & JOY_RIGHT;
11877   int up	= player_action & JOY_UP;
11878   int down	= player_action & JOY_DOWN;
11879   int button1	= player_action & JOY_BUTTON_1;
11880   int button2	= player_action & JOY_BUTTON_2;
11881   int dx	= (left ? -1 : right ? 1 : 0);
11882   int dy	= (up   ? -1 : down  ? 1 : 0);
11883 
11884   if (!player->active || tape.pausing)
11885     return 0;
11886 
11887   if (player_action)
11888   {
11889     if (button1)
11890       SnapField(player, dx, dy);
11891     else
11892     {
11893       if (button2)
11894 	DropElement(player);
11895 
11896       MovePlayer(player, dx, dy);
11897     }
11898 
11899     CheckSingleStepMode(player);
11900 
11901     SetPlayerWaiting(player, FALSE);
11902 
11903     return player_action;
11904   }
11905   else
11906   {
11907     /* no actions for this player (no input at player's configured device) */
11908 
11909     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11910     SnapField(player, 0, 0);
11911     CheckGravityMovementWhenNotMoving(player);
11912 
11913     if (player->MovPos == 0)
11914       SetPlayerWaiting(player, TRUE);
11915 
11916     if (player->MovPos == 0)	/* needed for tape.playing */
11917       player->is_moving = FALSE;
11918 
11919     player->is_dropping = FALSE;
11920     player->is_dropping_pressed = FALSE;
11921     player->drop_pressed_delay = 0;
11922 
11923     CheckSingleStepMode(player);
11924 
11925     return 0;
11926   }
11927 }
11928 
CheckLevelTime()11929 static void CheckLevelTime()
11930 {
11931   int i;
11932 
11933   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
11934   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11935   {
11936     if (level.native_em_level->lev->home == 0)	/* all players at home */
11937     {
11938       PlayerWins(local_player);
11939 
11940       AllPlayersGone = TRUE;
11941 
11942       level.native_em_level->lev->home = -1;
11943     }
11944 
11945     if (level.native_em_level->ply[0]->alive == 0 &&
11946 	level.native_em_level->ply[1]->alive == 0 &&
11947 	level.native_em_level->ply[2]->alive == 0 &&
11948 	level.native_em_level->ply[3]->alive == 0)	/* all dead */
11949       AllPlayersGone = TRUE;
11950   }
11951   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11952   {
11953     if (game_sp.LevelSolved &&
11954 	!game_sp.GameOver)				/* game won */
11955     {
11956       PlayerWins(local_player);
11957 
11958       game_sp.GameOver = TRUE;
11959 
11960       AllPlayersGone = TRUE;
11961     }
11962 
11963     if (game_sp.GameOver)				/* game lost */
11964       AllPlayersGone = TRUE;
11965   }
11966 
11967   if (TimeFrames >= FRAMES_PER_SECOND)
11968   {
11969     TimeFrames = 0;
11970     TapeTime++;
11971 
11972     for (i = 0; i < MAX_PLAYERS; i++)
11973     {
11974       struct PlayerInfo *player = &stored_player[i];
11975 
11976       if (SHIELD_ON(player))
11977       {
11978 	player->shield_normal_time_left--;
11979 
11980 	if (player->shield_deadly_time_left > 0)
11981 	  player->shield_deadly_time_left--;
11982       }
11983     }
11984 
11985     if (!local_player->LevelSolved && !level.use_step_counter)
11986     {
11987       TimePlayed++;
11988 
11989       if (TimeLeft > 0)
11990       {
11991 	TimeLeft--;
11992 
11993 	if (TimeLeft <= 10 && setup.time_limit)
11994 	  PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11995 
11996 #if 1
11997 	/* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11998 	   is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11999 
12000 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12001 
12002 	/* (already called by UpdateAndDisplayGameControlValues() below) */
12003 	// DisplayGameControlValues();
12004 #else
12005 	DrawGameValue_Time(TimeLeft);
12006 #endif
12007 
12008 	if (!TimeLeft && setup.time_limit)
12009 	{
12010 	  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12011 	    level.native_em_level->lev->killed_out_of_time = TRUE;
12012 	  else
12013 	    for (i = 0; i < MAX_PLAYERS; i++)
12014 	      KillPlayer(&stored_player[i]);
12015 	}
12016       }
12017 #if 1
12018       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12019       {
12020 	game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12021 
12022 	/* (already called by UpdateAndDisplayGameControlValues() below) */
12023 	// DisplayGameControlValues();
12024       }
12025 #else
12026       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12027 	DrawGameValue_Time(TimePlayed);
12028 #endif
12029 
12030       level.native_em_level->lev->time =
12031 	(game.no_time_limit ? TimePlayed : TimeLeft);
12032     }
12033 
12034     if (tape.recording || tape.playing)
12035       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12036   }
12037 
12038 #if 1
12039   UpdateAndDisplayGameControlValues();
12040 #else
12041   UpdateGameDoorValues();
12042   DrawGameDoorValues();
12043 #endif
12044 }
12045 
AdvanceFrameAndPlayerCounters(int player_nr)12046 void AdvanceFrameAndPlayerCounters(int player_nr)
12047 {
12048   int i;
12049 
12050   /* advance frame counters (global frame counter and time frame counter) */
12051   FrameCounter++;
12052   TimeFrames++;
12053 
12054   /* advance player counters (counters for move delay, move animation etc.) */
12055   for (i = 0; i < MAX_PLAYERS; i++)
12056   {
12057     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12058     int move_delay_value = stored_player[i].move_delay_value;
12059     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12060 
12061     if (!advance_player_counters)	/* not all players may be affected */
12062       continue;
12063 
12064 #if USE_NEW_PLAYER_ANIM
12065     if (move_frames == 0)	/* less than one move per game frame */
12066     {
12067       int stepsize = TILEX / move_delay_value;
12068       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12069       int count = (stored_player[i].is_moving ?
12070 		   ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12071 
12072       if (count % delay == 0)
12073 	move_frames = 1;
12074     }
12075 #endif
12076 
12077     stored_player[i].Frame += move_frames;
12078 
12079     if (stored_player[i].MovPos != 0)
12080       stored_player[i].StepFrame += move_frames;
12081 
12082     if (stored_player[i].move_delay > 0)
12083       stored_player[i].move_delay--;
12084 
12085     /* due to bugs in previous versions, counter must count up, not down */
12086     if (stored_player[i].push_delay != -1)
12087       stored_player[i].push_delay++;
12088 
12089     if (stored_player[i].drop_delay > 0)
12090       stored_player[i].drop_delay--;
12091 
12092     if (stored_player[i].is_dropping_pressed)
12093       stored_player[i].drop_pressed_delay++;
12094   }
12095 }
12096 
StartGameActions(boolean init_network_game,boolean record_tape,int random_seed)12097 void StartGameActions(boolean init_network_game, boolean record_tape,
12098 		      int random_seed)
12099 {
12100   unsigned int new_random_seed = InitRND(random_seed);
12101 
12102   if (record_tape)
12103     TapeStartRecording(new_random_seed);
12104 
12105 #if defined(NETWORK_AVALIABLE)
12106   if (init_network_game)
12107   {
12108     SendToServer_StartPlaying();
12109 
12110     return;
12111   }
12112 #endif
12113 
12114   InitGame();
12115 }
12116 
GameActions()12117 void GameActions()
12118 {
12119   static unsigned int game_frame_delay = 0;
12120   unsigned int game_frame_delay_value;
12121   byte *recorded_player_action;
12122   byte summarized_player_action = 0;
12123   byte tape_action[MAX_PLAYERS];
12124   int i;
12125 
12126   /* detect endless loops, caused by custom element programming */
12127   if (recursion_loop_detected && recursion_loop_depth == 0)
12128   {
12129     char *message = getStringCat3("Internal Error ! Element ",
12130 				  EL_NAME(recursion_loop_element),
12131 				  " caused endless loop ! Quit the game ?");
12132 
12133     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12134 	  EL_NAME(recursion_loop_element));
12135 
12136     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12137 
12138     recursion_loop_detected = FALSE;	/* if game should be continued */
12139 
12140     free(message);
12141 
12142     return;
12143   }
12144 
12145   if (game.restart_level)
12146     StartGameActions(options.network, setup.autorecord, level.random_seed);
12147 
12148   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12149   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150   {
12151     if (level.native_em_level->lev->home == 0)	/* all players at home */
12152     {
12153       PlayerWins(local_player);
12154 
12155       AllPlayersGone = TRUE;
12156 
12157       level.native_em_level->lev->home = -1;
12158     }
12159 
12160     if (level.native_em_level->ply[0]->alive == 0 &&
12161 	level.native_em_level->ply[1]->alive == 0 &&
12162 	level.native_em_level->ply[2]->alive == 0 &&
12163 	level.native_em_level->ply[3]->alive == 0)	/* all dead */
12164       AllPlayersGone = TRUE;
12165   }
12166   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12167   {
12168     if (game_sp.LevelSolved &&
12169 	!game_sp.GameOver)				/* game won */
12170     {
12171       PlayerWins(local_player);
12172 
12173       game_sp.GameOver = TRUE;
12174 
12175       AllPlayersGone = TRUE;
12176     }
12177 
12178     if (game_sp.GameOver)				/* game lost */
12179       AllPlayersGone = TRUE;
12180   }
12181 
12182   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12183     GameWon();
12184 
12185   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12186     TapeStop();
12187 
12188   if (game_status != GAME_MODE_PLAYING)		/* status might have changed */
12189     return;
12190 
12191   game_frame_delay_value =
12192     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12193 
12194   if (tape.playing && tape.warp_forward && !tape.pausing)
12195     game_frame_delay_value = 0;
12196 
12197   /* ---------- main game synchronization point ---------- */
12198 
12199   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12200 
12201   if (network_playing && !network_player_action_received)
12202   {
12203     /* try to get network player actions in time */
12204 
12205 #if defined(NETWORK_AVALIABLE)
12206     /* last chance to get network player actions without main loop delay */
12207     HandleNetworking();
12208 #endif
12209 
12210     /* game was quit by network peer */
12211     if (game_status != GAME_MODE_PLAYING)
12212       return;
12213 
12214     if (!network_player_action_received)
12215       return;		/* failed to get network player actions in time */
12216 
12217     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12218   }
12219 
12220   if (tape.pausing)
12221     return;
12222 
12223   /* at this point we know that we really continue executing the game */
12224 
12225   network_player_action_received = FALSE;
12226 
12227   /* when playing tape, read previously recorded player input from tape data */
12228   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12229 
12230 #if 1
12231   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12232   if (tape.pausing)
12233     return;
12234 #endif
12235 
12236   if (tape.set_centered_player)
12237   {
12238     game.centered_player_nr_next = tape.centered_player_nr_next;
12239     game.set_centered_player = TRUE;
12240   }
12241 
12242   for (i = 0; i < MAX_PLAYERS; i++)
12243   {
12244     summarized_player_action |= stored_player[i].action;
12245 
12246     if (!network_playing)
12247       stored_player[i].effective_action = stored_player[i].action;
12248   }
12249 
12250 #if defined(NETWORK_AVALIABLE)
12251   if (network_playing)
12252     SendToServer_MovePlayer(summarized_player_action);
12253 #endif
12254 
12255   if (!options.network && !setup.team_mode)
12256     local_player->effective_action = summarized_player_action;
12257 
12258   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12259   {
12260     for (i = 0; i < MAX_PLAYERS; i++)
12261       stored_player[i].effective_action =
12262 	(i == game.centered_player_nr ? summarized_player_action : 0);
12263   }
12264 
12265   if (recorded_player_action != NULL)
12266     for (i = 0; i < MAX_PLAYERS; i++)
12267       stored_player[i].effective_action = recorded_player_action[i];
12268 
12269   for (i = 0; i < MAX_PLAYERS; i++)
12270   {
12271     tape_action[i] = stored_player[i].effective_action;
12272 
12273     /* (this can only happen in the R'n'D game engine) */
12274     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12275       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12276   }
12277 
12278   /* only record actions from input devices, but not programmed actions */
12279   if (tape.recording)
12280     TapeRecordAction(tape_action);
12281 
12282 #if USE_NEW_PLAYER_ASSIGNMENTS
12283   {
12284     byte mapped_action[MAX_PLAYERS];
12285 
12286     for (i = 0; i < MAX_PLAYERS; i++)
12287       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12288 
12289     for (i = 0; i < MAX_PLAYERS; i++)
12290       stored_player[i].effective_action = mapped_action[i];
12291   }
12292 #endif
12293 
12294   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12295   {
12296     GameActions_EM_Main();
12297   }
12298   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12299   {
12300     GameActions_SP_Main();
12301   }
12302   else
12303   {
12304     GameActions_RND();
12305   }
12306 }
12307 
GameActions_EM_Main()12308 void GameActions_EM_Main()
12309 {
12310   byte effective_action[MAX_PLAYERS];
12311   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12312   int i;
12313 
12314   for (i = 0; i < MAX_PLAYERS; i++)
12315     effective_action[i] = stored_player[i].effective_action;
12316 
12317   GameActions_EM(effective_action, warp_mode);
12318 
12319   CheckLevelTime();
12320 
12321   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
12322 }
12323 
GameActions_SP_Main()12324 void GameActions_SP_Main()
12325 {
12326   byte effective_action[MAX_PLAYERS];
12327   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12328   int i;
12329 
12330   for (i = 0; i < MAX_PLAYERS; i++)
12331     effective_action[i] = stored_player[i].effective_action;
12332 
12333   GameActions_SP(effective_action, warp_mode);
12334 
12335   CheckLevelTime();
12336 
12337   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
12338 }
12339 
GameActions_RND()12340 void GameActions_RND()
12341 {
12342   int magic_wall_x = 0, magic_wall_y = 0;
12343   int i, x, y, element, graphic;
12344 
12345   InitPlayfieldScanModeVars();
12346 
12347 #if USE_ONE_MORE_CHANGE_PER_FRAME
12348   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12349   {
12350     SCAN_PLAYFIELD(x, y)
12351     {
12352       ChangeCount[x][y] = 0;
12353       ChangeEvent[x][y] = -1;
12354     }
12355   }
12356 #endif
12357 
12358   if (game.set_centered_player)
12359   {
12360     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12361 
12362     /* switching to "all players" only possible if all players fit to screen */
12363     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12364     {
12365       game.centered_player_nr_next = game.centered_player_nr;
12366       game.set_centered_player = FALSE;
12367     }
12368 
12369     /* do not switch focus to non-existing (or non-active) player */
12370     if (game.centered_player_nr_next >= 0 &&
12371 	!stored_player[game.centered_player_nr_next].active)
12372     {
12373       game.centered_player_nr_next = game.centered_player_nr;
12374       game.set_centered_player = FALSE;
12375     }
12376   }
12377 
12378   if (game.set_centered_player &&
12379       ScreenMovPos == 0)	/* screen currently aligned at tile position */
12380   {
12381     int sx, sy;
12382 
12383     if (game.centered_player_nr_next == -1)
12384     {
12385       setScreenCenteredToAllPlayers(&sx, &sy);
12386     }
12387     else
12388     {
12389       sx = stored_player[game.centered_player_nr_next].jx;
12390       sy = stored_player[game.centered_player_nr_next].jy;
12391     }
12392 
12393     game.centered_player_nr = game.centered_player_nr_next;
12394     game.set_centered_player = FALSE;
12395 
12396     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12397     DrawGameDoorValues();
12398   }
12399 
12400   for (i = 0; i < MAX_PLAYERS; i++)
12401   {
12402     int actual_player_action = stored_player[i].effective_action;
12403 
12404 #if 1
12405     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12406        - rnd_equinox_tetrachloride 048
12407        - rnd_equinox_tetrachloride_ii 096
12408        - rnd_emanuel_schmieg 002
12409        - doctor_sloan_ww 001, 020
12410     */
12411     if (stored_player[i].MovPos == 0)
12412       CheckGravityMovement(&stored_player[i]);
12413 #endif
12414 
12415     /* overwrite programmed action with tape action */
12416     if (stored_player[i].programmed_action)
12417       actual_player_action = stored_player[i].programmed_action;
12418 
12419     PlayerActions(&stored_player[i], actual_player_action);
12420 
12421     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12422   }
12423 
12424   ScrollScreen(NULL, SCROLL_GO_ON);
12425 
12426   /* for backwards compatibility, the following code emulates a fixed bug that
12427      occured when pushing elements (causing elements that just made their last
12428      pushing step to already (if possible) make their first falling step in the
12429      same game frame, which is bad); this code is also needed to use the famous
12430      "spring push bug" which is used in older levels and might be wanted to be
12431      used also in newer levels, but in this case the buggy pushing code is only
12432      affecting the "spring" element and no other elements */
12433 
12434   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12435   {
12436     for (i = 0; i < MAX_PLAYERS; i++)
12437     {
12438       struct PlayerInfo *player = &stored_player[i];
12439       int x = player->jx;
12440       int y = player->jy;
12441 
12442       if (player->active && player->is_pushing && player->is_moving &&
12443 	  IS_MOVING(x, y) &&
12444 	  (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12445 	   Feld[x][y] == EL_SPRING))
12446       {
12447 	ContinueMoving(x, y);
12448 
12449 	/* continue moving after pushing (this is actually a bug) */
12450 	if (!IS_MOVING(x, y))
12451 	  Stop[x][y] = FALSE;
12452       }
12453     }
12454   }
12455 
12456 #if 0
12457   debug_print_timestamp(0, "start main loop profiling");
12458 #endif
12459 
12460   SCAN_PLAYFIELD(x, y)
12461   {
12462     ChangeCount[x][y] = 0;
12463     ChangeEvent[x][y] = -1;
12464 
12465     /* this must be handled before main playfield loop */
12466     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12467     {
12468       MovDelay[x][y]--;
12469       if (MovDelay[x][y] <= 0)
12470 	RemoveField(x, y);
12471     }
12472 
12473 #if USE_NEW_SNAP_DELAY
12474     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12475     {
12476       MovDelay[x][y]--;
12477       if (MovDelay[x][y] <= 0)
12478       {
12479 	RemoveField(x, y);
12480 	TEST_DrawLevelField(x, y);
12481 
12482 	TestIfElementTouchesCustomElement(x, y);	/* for empty space */
12483       }
12484     }
12485 #endif
12486 
12487 #if DEBUG
12488     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12489     {
12490       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12491       printf("GameActions(): This should never happen!\n");
12492 
12493       ChangePage[x][y] = -1;
12494     }
12495 #endif
12496 
12497     Stop[x][y] = FALSE;
12498     if (WasJustMoving[x][y] > 0)
12499       WasJustMoving[x][y]--;
12500     if (WasJustFalling[x][y] > 0)
12501       WasJustFalling[x][y]--;
12502     if (CheckCollision[x][y] > 0)
12503       CheckCollision[x][y]--;
12504     if (CheckImpact[x][y] > 0)
12505       CheckImpact[x][y]--;
12506 
12507     GfxFrame[x][y]++;
12508 
12509     /* reset finished pushing action (not done in ContinueMoving() to allow
12510        continuous pushing animation for elements with zero push delay) */
12511     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12512     {
12513       ResetGfxAnimation(x, y);
12514       TEST_DrawLevelField(x, y);
12515     }
12516 
12517 #if DEBUG
12518     if (IS_BLOCKED(x, y))
12519     {
12520       int oldx, oldy;
12521 
12522       Blocked2Moving(x, y, &oldx, &oldy);
12523       if (!IS_MOVING(oldx, oldy))
12524       {
12525 	printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12526 	printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12527 	printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12528 	printf("GameActions(): This should never happen!\n");
12529       }
12530     }
12531 #endif
12532   }
12533 
12534 #if 0
12535   debug_print_timestamp(0, "- time for pre-main loop:");
12536 #endif
12537 
12538 #if 0	// -------------------- !!! TEST ONLY !!! --------------------
12539   SCAN_PLAYFIELD(x, y)
12540   {
12541     element = Feld[x][y];
12542     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12543 
12544 #if 1
12545     {
12546 #if 1
12547       int element2 = element;
12548       int graphic2 = graphic;
12549 #else
12550       int element2 = Feld[x][y];
12551       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12552 #endif
12553       int last_gfx_frame = GfxFrame[x][y];
12554 
12555       if (graphic_info[graphic2].anim_global_sync)
12556 	GfxFrame[x][y] = FrameCounter;
12557       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12558 	GfxFrame[x][y] = CustomValue[x][y];
12559       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12560 	GfxFrame[x][y] = element_info[element2].collect_score;
12561       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12562 	GfxFrame[x][y] = ChangeDelay[x][y];
12563 
12564       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12565 	DrawLevelGraphicAnimation(x, y, graphic2);
12566     }
12567 #else
12568     ResetGfxFrame(x, y, TRUE);
12569 #endif
12570 
12571 #if 1
12572     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12573 	IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12574       ResetRandomAnimationValue(x, y);
12575 #endif
12576 
12577 #if 1
12578     SetRandomAnimationValue(x, y);
12579 #endif
12580 
12581 #if 1
12582     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12583 #endif
12584   }
12585 #endif	// -------------------- !!! TEST ONLY !!! --------------------
12586 
12587 #if 0
12588   debug_print_timestamp(0, "- time for TEST loop:     -->");
12589 #endif
12590 
12591   SCAN_PLAYFIELD(x, y)
12592   {
12593     element = Feld[x][y];
12594     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12595 
12596     ResetGfxFrame(x, y, TRUE);
12597 
12598     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12599 	IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12600       ResetRandomAnimationValue(x, y);
12601 
12602     SetRandomAnimationValue(x, y);
12603 
12604     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12605 
12606     if (IS_INACTIVE(element))
12607     {
12608       if (IS_ANIMATED(graphic))
12609 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12610 
12611       continue;
12612     }
12613 
12614     /* this may take place after moving, so 'element' may have changed */
12615     if (IS_CHANGING(x, y) &&
12616 	(game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12617     {
12618       int page = element_info[element].event_page_nr[CE_DELAY];
12619 
12620 #if 1
12621       HandleElementChange(x, y, page);
12622 #else
12623       if (CAN_CHANGE(element))
12624 	HandleElementChange(x, y, page);
12625 
12626       if (HAS_ACTION(element))
12627 	ExecuteCustomElementAction(x, y, element, page);
12628 #endif
12629 
12630       element = Feld[x][y];
12631       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12632     }
12633 
12634 #if 0	// ---------------------------------------------------------------------
12635 
12636     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12637     {
12638       StartMoving(x, y);
12639 
12640       element = Feld[x][y];
12641       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12642 
12643       if (IS_ANIMATED(graphic) &&
12644 	  !IS_MOVING(x, y) &&
12645 	  !Stop[x][y])
12646 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12647 
12648       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12649 	TEST_DrawTwinkleOnField(x, y);
12650     }
12651     else if (IS_MOVING(x, y))
12652       ContinueMoving(x, y);
12653     else
12654     {
12655       switch (element)
12656       {
12657         case EL_ACID:
12658         case EL_EXIT_OPEN:
12659         case EL_EM_EXIT_OPEN:
12660         case EL_SP_EXIT_OPEN:
12661         case EL_STEEL_EXIT_OPEN:
12662         case EL_EM_STEEL_EXIT_OPEN:
12663         case EL_SP_TERMINAL:
12664         case EL_SP_TERMINAL_ACTIVE:
12665         case EL_EXTRA_TIME:
12666         case EL_SHIELD_NORMAL:
12667         case EL_SHIELD_DEADLY:
12668 	  if (IS_ANIMATED(graphic))
12669 	    DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12670 	  break;
12671 
12672         case EL_DYNAMITE_ACTIVE:
12673         case EL_EM_DYNAMITE_ACTIVE:
12674         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12675         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12676         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12677         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12678         case EL_SP_DISK_RED_ACTIVE:
12679 	  CheckDynamite(x, y);
12680 	  break;
12681 
12682         case EL_AMOEBA_GROWING:
12683 	  AmoebeWaechst(x, y);
12684 	  break;
12685 
12686         case EL_AMOEBA_SHRINKING:
12687 	  AmoebaDisappearing(x, y);
12688 	  break;
12689 
12690 #if !USE_NEW_AMOEBA_CODE
12691         case EL_AMOEBA_WET:
12692         case EL_AMOEBA_DRY:
12693         case EL_AMOEBA_FULL:
12694         case EL_BD_AMOEBA:
12695         case EL_EMC_DRIPPER:
12696 	  AmoebeAbleger(x, y);
12697 	  break;
12698 #endif
12699 
12700         case EL_GAME_OF_LIFE:
12701         case EL_BIOMAZE:
12702 	  Life(x, y);
12703 	  break;
12704 
12705         case EL_EXIT_CLOSED:
12706 	  CheckExit(x, y);
12707 	  break;
12708 
12709         case EL_EM_EXIT_CLOSED:
12710 	  CheckExitEM(x, y);
12711 	  break;
12712 
12713         case EL_STEEL_EXIT_CLOSED:
12714 	  CheckExitSteel(x, y);
12715 	  break;
12716 
12717         case EL_EM_STEEL_EXIT_CLOSED:
12718 	  CheckExitSteelEM(x, y);
12719 	  break;
12720 
12721         case EL_SP_EXIT_CLOSED:
12722 	  CheckExitSP(x, y);
12723 	  break;
12724 
12725         case EL_EXPANDABLE_WALL_GROWING:
12726         case EL_EXPANDABLE_STEELWALL_GROWING:
12727 	  MauerWaechst(x, y);
12728 	  break;
12729 
12730         case EL_EXPANDABLE_WALL:
12731         case EL_EXPANDABLE_WALL_HORIZONTAL:
12732         case EL_EXPANDABLE_WALL_VERTICAL:
12733         case EL_EXPANDABLE_WALL_ANY:
12734         case EL_BD_EXPANDABLE_WALL:
12735 	  MauerAbleger(x, y);
12736 	  break;
12737 
12738         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12739         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12740         case EL_EXPANDABLE_STEELWALL_ANY:
12741 	  MauerAblegerStahl(x, y);
12742 	  break;
12743 
12744         case EL_FLAMES:
12745 	  CheckForDragon(x, y);
12746 	  break;
12747 
12748         case EL_EXPLOSION:
12749 	  break;
12750 
12751         case EL_ELEMENT_SNAPPING:
12752         case EL_DIAGONAL_SHRINKING:
12753         case EL_DIAGONAL_GROWING:
12754 	{
12755 	  graphic =
12756 	    el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12757 
12758 	  DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12759 	  break;
12760 	}
12761 
12762         default:
12763 	  if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12764 	    DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12765 	  break;
12766       }
12767     }
12768 
12769 #else	// ---------------------------------------------------------------------
12770 
12771     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12772     {
12773       StartMoving(x, y);
12774 
12775       element = Feld[x][y];
12776       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12777 
12778       if (IS_ANIMATED(graphic) &&
12779 	  !IS_MOVING(x, y) &&
12780 	  !Stop[x][y])
12781 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12782 
12783       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12784 	TEST_DrawTwinkleOnField(x, y);
12785     }
12786     else if ((element == EL_ACID ||
12787 	      element == EL_EXIT_OPEN ||
12788 	      element == EL_EM_EXIT_OPEN ||
12789 	      element == EL_SP_EXIT_OPEN ||
12790 	      element == EL_STEEL_EXIT_OPEN ||
12791 	      element == EL_EM_STEEL_EXIT_OPEN ||
12792 	      element == EL_SP_TERMINAL ||
12793 	      element == EL_SP_TERMINAL_ACTIVE ||
12794 	      element == EL_EXTRA_TIME ||
12795 	      element == EL_SHIELD_NORMAL ||
12796 	      element == EL_SHIELD_DEADLY) &&
12797 	     IS_ANIMATED(graphic))
12798       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12799     else if (IS_MOVING(x, y))
12800       ContinueMoving(x, y);
12801     else if (IS_ACTIVE_BOMB(element))
12802       CheckDynamite(x, y);
12803     else if (element == EL_AMOEBA_GROWING)
12804       AmoebeWaechst(x, y);
12805     else if (element == EL_AMOEBA_SHRINKING)
12806       AmoebaDisappearing(x, y);
12807 
12808 #if !USE_NEW_AMOEBA_CODE
12809     else if (IS_AMOEBALIVE(element))
12810       AmoebeAbleger(x, y);
12811 #endif
12812 
12813     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12814       Life(x, y);
12815     else if (element == EL_EXIT_CLOSED)
12816       CheckExit(x, y);
12817     else if (element == EL_EM_EXIT_CLOSED)
12818       CheckExitEM(x, y);
12819     else if (element == EL_STEEL_EXIT_CLOSED)
12820       CheckExitSteel(x, y);
12821     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12822       CheckExitSteelEM(x, y);
12823     else if (element == EL_SP_EXIT_CLOSED)
12824       CheckExitSP(x, y);
12825     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12826 	     element == EL_EXPANDABLE_STEELWALL_GROWING)
12827       MauerWaechst(x, y);
12828     else if (element == EL_EXPANDABLE_WALL ||
12829 	     element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12830 	     element == EL_EXPANDABLE_WALL_VERTICAL ||
12831 	     element == EL_EXPANDABLE_WALL_ANY ||
12832 	     element == EL_BD_EXPANDABLE_WALL)
12833       MauerAbleger(x, y);
12834     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12835 	     element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12836 	     element == EL_EXPANDABLE_STEELWALL_ANY)
12837       MauerAblegerStahl(x, y);
12838     else if (element == EL_FLAMES)
12839       CheckForDragon(x, y);
12840     else if (element == EL_EXPLOSION)
12841       ;	/* drawing of correct explosion animation is handled separately */
12842     else if (element == EL_ELEMENT_SNAPPING ||
12843 	     element == EL_DIAGONAL_SHRINKING ||
12844 	     element == EL_DIAGONAL_GROWING)
12845     {
12846       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12847 
12848       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12849     }
12850     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12851       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12852 
12853 #endif	// ---------------------------------------------------------------------
12854 
12855     if (IS_BELT_ACTIVE(element))
12856       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12857 
12858     if (game.magic_wall_active)
12859     {
12860       int jx = local_player->jx, jy = local_player->jy;
12861 
12862       /* play the element sound at the position nearest to the player */
12863       if ((element == EL_MAGIC_WALL_FULL ||
12864 	   element == EL_MAGIC_WALL_ACTIVE ||
12865 	   element == EL_MAGIC_WALL_EMPTYING ||
12866 	   element == EL_BD_MAGIC_WALL_FULL ||
12867 	   element == EL_BD_MAGIC_WALL_ACTIVE ||
12868 	   element == EL_BD_MAGIC_WALL_EMPTYING ||
12869 	   element == EL_DC_MAGIC_WALL_FULL ||
12870 	   element == EL_DC_MAGIC_WALL_ACTIVE ||
12871 	   element == EL_DC_MAGIC_WALL_EMPTYING) &&
12872 	  ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12873       {
12874 	magic_wall_x = x;
12875 	magic_wall_y = y;
12876       }
12877     }
12878   }
12879 
12880 #if 0
12881   debug_print_timestamp(0, "- time for MAIN loop:     -->");
12882 #endif
12883 
12884 #if USE_NEW_AMOEBA_CODE
12885   /* new experimental amoeba growth stuff */
12886   if (!(FrameCounter % 8))
12887   {
12888     static unsigned int random = 1684108901;
12889 
12890     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12891     {
12892       x = RND(lev_fieldx);
12893       y = RND(lev_fieldy);
12894       element = Feld[x][y];
12895 
12896       if (!IS_PLAYER(x,y) &&
12897 	  (element == EL_EMPTY ||
12898 	   CAN_GROW_INTO(element) ||
12899 	   element == EL_QUICKSAND_EMPTY ||
12900 	   element == EL_QUICKSAND_FAST_EMPTY ||
12901 	   element == EL_ACID_SPLASH_LEFT ||
12902 	   element == EL_ACID_SPLASH_RIGHT))
12903       {
12904 	if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12905 	    (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12906 	    (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12907 	    (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12908 	  Feld[x][y] = EL_AMOEBA_DROP;
12909       }
12910 
12911       random = random * 129 + 1;
12912     }
12913   }
12914 #endif
12915 
12916 #if 0
12917   if (game.explosions_delayed)
12918 #endif
12919   {
12920     game.explosions_delayed = FALSE;
12921 
12922     SCAN_PLAYFIELD(x, y)
12923     {
12924       element = Feld[x][y];
12925 
12926       if (ExplodeField[x][y])
12927 	Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12928       else if (element == EL_EXPLOSION)
12929 	Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12930 
12931       ExplodeField[x][y] = EX_TYPE_NONE;
12932     }
12933 
12934     game.explosions_delayed = TRUE;
12935   }
12936 
12937   if (game.magic_wall_active)
12938   {
12939     if (!(game.magic_wall_time_left % 4))
12940     {
12941       int element = Feld[magic_wall_x][magic_wall_y];
12942 
12943       if (element == EL_BD_MAGIC_WALL_FULL ||
12944 	  element == EL_BD_MAGIC_WALL_ACTIVE ||
12945 	  element == EL_BD_MAGIC_WALL_EMPTYING)
12946 	PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12947       else if (element == EL_DC_MAGIC_WALL_FULL ||
12948 	       element == EL_DC_MAGIC_WALL_ACTIVE ||
12949 	       element == EL_DC_MAGIC_WALL_EMPTYING)
12950 	PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12951       else
12952 	PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12953     }
12954 
12955     if (game.magic_wall_time_left > 0)
12956     {
12957       game.magic_wall_time_left--;
12958 
12959       if (!game.magic_wall_time_left)
12960       {
12961 	SCAN_PLAYFIELD(x, y)
12962 	{
12963 	  element = Feld[x][y];
12964 
12965 	  if (element == EL_MAGIC_WALL_ACTIVE ||
12966 	      element == EL_MAGIC_WALL_FULL)
12967 	  {
12968 	    Feld[x][y] = EL_MAGIC_WALL_DEAD;
12969 	    TEST_DrawLevelField(x, y);
12970 	  }
12971 	  else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12972 		   element == EL_BD_MAGIC_WALL_FULL)
12973 	  {
12974 	    Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
12975 	    TEST_DrawLevelField(x, y);
12976 	  }
12977 	  else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12978 		   element == EL_DC_MAGIC_WALL_FULL)
12979 	  {
12980 	    Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
12981 	    TEST_DrawLevelField(x, y);
12982 	  }
12983 	}
12984 
12985 	game.magic_wall_active = FALSE;
12986       }
12987     }
12988   }
12989 
12990   if (game.light_time_left > 0)
12991   {
12992     game.light_time_left--;
12993 
12994     if (game.light_time_left == 0)
12995       RedrawAllLightSwitchesAndInvisibleElements();
12996   }
12997 
12998   if (game.timegate_time_left > 0)
12999   {
13000     game.timegate_time_left--;
13001 
13002     if (game.timegate_time_left == 0)
13003       CloseAllOpenTimegates();
13004   }
13005 
13006   if (game.lenses_time_left > 0)
13007   {
13008     game.lenses_time_left--;
13009 
13010     if (game.lenses_time_left == 0)
13011       RedrawAllInvisibleElementsForLenses();
13012   }
13013 
13014   if (game.magnify_time_left > 0)
13015   {
13016     game.magnify_time_left--;
13017 
13018     if (game.magnify_time_left == 0)
13019       RedrawAllInvisibleElementsForMagnifier();
13020   }
13021 
13022   for (i = 0; i < MAX_PLAYERS; i++)
13023   {
13024     struct PlayerInfo *player = &stored_player[i];
13025 
13026     if (SHIELD_ON(player))
13027     {
13028       if (player->shield_deadly_time_left)
13029 	PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13030       else if (player->shield_normal_time_left)
13031 	PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13032     }
13033   }
13034 
13035 #if USE_DELAYED_GFX_REDRAW
13036   SCAN_PLAYFIELD(x, y)
13037   {
13038 #if 1
13039     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13040 #else
13041     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13042 	GfxRedraw[x][y] != GFX_REDRAW_NONE)
13043 #endif
13044     {
13045       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13046 	 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13047 
13048       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13049 	DrawLevelField(x, y);
13050 
13051       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13052 	DrawLevelFieldCrumbled(x, y);
13053 
13054       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13055 	DrawLevelFieldCrumbledNeighbours(x, y);
13056 
13057       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13058 	DrawTwinkleOnField(x, y);
13059     }
13060 
13061     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13062   }
13063 #endif
13064 
13065   CheckLevelTime();
13066 
13067   DrawAllPlayers();
13068   PlayAllPlayersSound();
13069 
13070   if (options.debug)			/* calculate frames per second */
13071   {
13072     static unsigned int fps_counter = 0;
13073     static int fps_frames = 0;
13074     unsigned int fps_delay_ms = Counter() - fps_counter;
13075 
13076     fps_frames++;
13077 
13078     if (fps_delay_ms >= 500)	/* calculate fps every 0.5 seconds */
13079     {
13080       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13081 
13082       fps_frames = 0;
13083       fps_counter = Counter();
13084     }
13085 
13086     redraw_mask |= REDRAW_FPS;
13087   }
13088 
13089   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
13090 
13091   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13092   {
13093     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13094 
13095     local_player->show_envelope = 0;
13096   }
13097 
13098 #if 0
13099   debug_print_timestamp(0, "stop main loop profiling ");
13100   printf("----------------------------------------------------------\n");
13101 #endif
13102 
13103   /* use random number generator in every frame to make it less predictable */
13104   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13105     RND(1);
13106 }
13107 
AllPlayersInSight(struct PlayerInfo * player,int x,int y)13108 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13109 {
13110   int min_x = x, min_y = y, max_x = x, max_y = y;
13111   int i;
13112 
13113   for (i = 0; i < MAX_PLAYERS; i++)
13114   {
13115     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13116 
13117     if (!stored_player[i].active || &stored_player[i] == player)
13118       continue;
13119 
13120     min_x = MIN(min_x, jx);
13121     min_y = MIN(min_y, jy);
13122     max_x = MAX(max_x, jx);
13123     max_y = MAX(max_y, jy);
13124   }
13125 
13126   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13127 }
13128 
AllPlayersInVisibleScreen()13129 static boolean AllPlayersInVisibleScreen()
13130 {
13131   int i;
13132 
13133   for (i = 0; i < MAX_PLAYERS; i++)
13134   {
13135     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13136 
13137     if (!stored_player[i].active)
13138       continue;
13139 
13140     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13141       return FALSE;
13142   }
13143 
13144   return TRUE;
13145 }
13146 
ScrollLevel(int dx,int dy)13147 void ScrollLevel(int dx, int dy)
13148 {
13149 #if 0
13150   /* (directly solved in BlitBitmap() now) */
13151   static Bitmap *bitmap_db_field2 = NULL;
13152   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13153   int x, y;
13154 #else
13155   int x, y;
13156 #endif
13157 
13158 #if 0
13159   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13160   /* only horizontal XOR vertical scroll direction allowed */
13161   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13162     return;
13163 #endif
13164 
13165 #if 0
13166   /* (directly solved in BlitBitmap() now) */
13167   if (bitmap_db_field2 == NULL)
13168     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13169 
13170   /* needed when blitting directly to same bitmap -- should not be needed with
13171      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13172   BlitBitmap(drawto_field, bitmap_db_field2,
13173 	     FX + TILEX * (dx == -1) - softscroll_offset,
13174 	     FY + TILEY * (dy == -1) - softscroll_offset,
13175 	     SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13176 	     SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13177 	     FX + TILEX * (dx == 1) - softscroll_offset,
13178 	     FY + TILEY * (dy == 1) - softscroll_offset);
13179   BlitBitmap(bitmap_db_field2, drawto_field,
13180 	     FX + TILEX * (dx == 1) - softscroll_offset,
13181 	     FY + TILEY * (dy == 1) - softscroll_offset,
13182 	     SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13183 	     SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13184 	     FX + TILEX * (dx == 1) - softscroll_offset,
13185 	     FY + TILEY * (dy == 1) - softscroll_offset);
13186 
13187 #else
13188 
13189 #if 0
13190   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13191   int xsize = (BX2 - BX1 + 1);
13192   int ysize = (BY2 - BY1 + 1);
13193   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13194   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13195   int step  = (start < end ? +1 : -1);
13196 
13197   for (i = start; i != end; i += step)
13198   {
13199     BlitBitmap(drawto_field, drawto_field,
13200 	       FX + TILEX * (dx != 0 ? i + step : 0),
13201 	       FY + TILEY * (dy != 0 ? i + step : 0),
13202 	       TILEX * (dx != 0 ? 1 : xsize),
13203 	       TILEY * (dy != 0 ? 1 : ysize),
13204 	       FX + TILEX * (dx != 0 ? i : 0),
13205 	       FY + TILEY * (dy != 0 ? i : 0));
13206   }
13207 
13208 #else
13209 
13210 #if NEW_TILESIZE
13211 #if NEW_SCROLL
13212   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
13213 #else
13214   int softscroll_offset = (setup.soft_scrolling ? TILEX_VAR : 0);
13215 #endif
13216 #else
13217 #if NEW_SCROLL
13218   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX : 0);
13219 #else
13220   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13221 #endif
13222 #endif
13223 
13224 #if NEW_TILESIZE
13225   BlitBitmap(drawto_field, drawto_field,
13226 	     FX + TILEX_VAR * (dx == -1) - softscroll_offset,
13227 	     FY + TILEY_VAR * (dy == -1) - softscroll_offset,
13228 	     SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
13229 	     SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
13230 	     FX + TILEX_VAR * (dx == 1) - softscroll_offset,
13231 	     FY + TILEY_VAR * (dy == 1) - softscroll_offset);
13232 #else
13233   BlitBitmap(drawto_field, drawto_field,
13234 	     FX + TILEX * (dx == -1) - softscroll_offset,
13235 	     FY + TILEY * (dy == -1) - softscroll_offset,
13236 	     SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13237 	     SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13238 	     FX + TILEX * (dx == 1) - softscroll_offset,
13239 	     FY + TILEY * (dy == 1) - softscroll_offset);
13240 #endif
13241 
13242 #endif
13243 #endif
13244 
13245   if (dx != 0)
13246   {
13247     x = (dx == 1 ? BX1 : BX2);
13248     for (y = BY1; y <= BY2; y++)
13249       DrawScreenField(x, y);
13250   }
13251 
13252   if (dy != 0)
13253   {
13254     y = (dy == 1 ? BY1 : BY2);
13255     for (x = BX1; x <= BX2; x++)
13256       DrawScreenField(x, y);
13257   }
13258 
13259   redraw_mask |= REDRAW_FIELD;
13260 }
13261 
canFallDown(struct PlayerInfo * player)13262 static boolean canFallDown(struct PlayerInfo *player)
13263 {
13264   int jx = player->jx, jy = player->jy;
13265 
13266   return (IN_LEV_FIELD(jx, jy + 1) &&
13267 	  (IS_FREE(jx, jy + 1) ||
13268 	   (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13269 	  IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13270 	  !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13271 }
13272 
canPassField(int x,int y,int move_dir)13273 static boolean canPassField(int x, int y, int move_dir)
13274 {
13275   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13276   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13277   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13278   int nextx = x + dx;
13279   int nexty = y + dy;
13280   int element = Feld[x][y];
13281 
13282   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13283 	  !CAN_MOVE(element) &&
13284 	  IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13285 	  IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13286 	  (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13287 }
13288 
canMoveToValidFieldWithGravity(int x,int y,int move_dir)13289 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13290 {
13291   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13292   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13293   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13294   int newx = x + dx;
13295   int newy = y + dy;
13296 
13297   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13298 	  IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13299 	  (IS_DIGGABLE(Feld[newx][newy]) ||
13300 	   IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13301 	   canPassField(newx, newy, move_dir)));
13302 }
13303 
CheckGravityMovement(struct PlayerInfo * player)13304 static void CheckGravityMovement(struct PlayerInfo *player)
13305 {
13306 #if USE_PLAYER_GRAVITY
13307   if (player->gravity && !player->programmed_action)
13308 #else
13309   if (game.gravity && !player->programmed_action)
13310 #endif
13311   {
13312     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13313     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13314     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13315     int jx = player->jx, jy = player->jy;
13316     boolean player_is_moving_to_valid_field =
13317       (!player_is_snapping &&
13318        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13319 	canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13320     boolean player_can_fall_down = canFallDown(player);
13321 
13322     if (player_can_fall_down &&
13323 	!player_is_moving_to_valid_field)
13324       player->programmed_action = MV_DOWN;
13325   }
13326 }
13327 
CheckGravityMovementWhenNotMoving(struct PlayerInfo * player)13328 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13329 {
13330   return CheckGravityMovement(player);
13331 
13332 #if USE_PLAYER_GRAVITY
13333   if (player->gravity && !player->programmed_action)
13334 #else
13335   if (game.gravity && !player->programmed_action)
13336 #endif
13337   {
13338     int jx = player->jx, jy = player->jy;
13339     boolean field_under_player_is_free =
13340       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13341     boolean player_is_standing_on_valid_field =
13342       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13343        (IS_WALKABLE(Feld[jx][jy]) &&
13344 	!(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13345 
13346     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13347       player->programmed_action = MV_DOWN;
13348   }
13349 }
13350 
13351 /*
13352   MovePlayerOneStep()
13353   -----------------------------------------------------------------------------
13354   dx, dy:		direction (non-diagonal) to try to move the player to
13355   real_dx, real_dy:	direction as read from input device (can be diagonal)
13356 */
13357 
MovePlayerOneStep(struct PlayerInfo * player,int dx,int dy,int real_dx,int real_dy)13358 boolean MovePlayerOneStep(struct PlayerInfo *player,
13359 			  int dx, int dy, int real_dx, int real_dy)
13360 {
13361   int jx = player->jx, jy = player->jy;
13362   int new_jx = jx + dx, new_jy = jy + dy;
13363 #if !USE_FIXED_DONT_RUN_INTO
13364   int element;
13365 #endif
13366   int can_move;
13367   boolean player_can_move = !player->cannot_move;
13368 
13369   if (!player->active || (!dx && !dy))
13370     return MP_NO_ACTION;
13371 
13372   player->MovDir = (dx < 0 ? MV_LEFT :
13373 		    dx > 0 ? MV_RIGHT :
13374 		    dy < 0 ? MV_UP :
13375 		    dy > 0 ? MV_DOWN :	MV_NONE);
13376 
13377   if (!IN_LEV_FIELD(new_jx, new_jy))
13378     return MP_NO_ACTION;
13379 
13380   if (!player_can_move)
13381   {
13382     if (player->MovPos == 0)
13383     {
13384       player->is_moving = FALSE;
13385       player->is_digging = FALSE;
13386       player->is_collecting = FALSE;
13387       player->is_snapping = FALSE;
13388       player->is_pushing = FALSE;
13389     }
13390   }
13391 
13392 #if 1
13393   if (!options.network && game.centered_player_nr == -1 &&
13394       !AllPlayersInSight(player, new_jx, new_jy))
13395     return MP_NO_ACTION;
13396 #else
13397   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13398     return MP_NO_ACTION;
13399 #endif
13400 
13401 #if !USE_FIXED_DONT_RUN_INTO
13402   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13403 
13404   /* (moved to DigField()) */
13405   if (player_can_move && DONT_RUN_INTO(element))
13406   {
13407     if (element == EL_ACID && dx == 0 && dy == 1)
13408     {
13409       SplashAcid(new_jx, new_jy);
13410       Feld[jx][jy] = EL_PLAYER_1;
13411       InitMovingField(jx, jy, MV_DOWN);
13412       Store[jx][jy] = EL_ACID;
13413       ContinueMoving(jx, jy);
13414       BuryPlayer(player);
13415     }
13416     else
13417       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13418 
13419     return MP_MOVING;
13420   }
13421 #endif
13422 
13423   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13424   if (can_move != MP_MOVING)
13425     return can_move;
13426 
13427   /* check if DigField() has caused relocation of the player */
13428   if (player->jx != jx || player->jy != jy)
13429     return MP_NO_ACTION;	/* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13430 
13431   StorePlayer[jx][jy] = 0;
13432   player->last_jx = jx;
13433   player->last_jy = jy;
13434   player->jx = new_jx;
13435   player->jy = new_jy;
13436   StorePlayer[new_jx][new_jy] = player->element_nr;
13437 
13438   if (player->move_delay_value_next != -1)
13439   {
13440     player->move_delay_value = player->move_delay_value_next;
13441     player->move_delay_value_next = -1;
13442   }
13443 
13444   player->MovPos =
13445     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13446 
13447   player->step_counter++;
13448 
13449   PlayerVisit[jx][jy] = FrameCounter;
13450 
13451 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13452   player->is_moving = TRUE;
13453 #endif
13454 
13455 #if 1
13456   /* should better be called in MovePlayer(), but this breaks some tapes */
13457   ScrollPlayer(player, SCROLL_INIT);
13458 #endif
13459 
13460   return MP_MOVING;
13461 }
13462 
MovePlayer(struct PlayerInfo * player,int dx,int dy)13463 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13464 {
13465   int jx = player->jx, jy = player->jy;
13466   int old_jx = jx, old_jy = jy;
13467   int moved = MP_NO_ACTION;
13468 
13469   if (!player->active)
13470     return FALSE;
13471 
13472   if (!dx && !dy)
13473   {
13474     if (player->MovPos == 0)
13475     {
13476       player->is_moving = FALSE;
13477       player->is_digging = FALSE;
13478       player->is_collecting = FALSE;
13479       player->is_snapping = FALSE;
13480       player->is_pushing = FALSE;
13481     }
13482 
13483     return FALSE;
13484   }
13485 
13486   if (player->move_delay > 0)
13487     return FALSE;
13488 
13489   player->move_delay = -1;		/* set to "uninitialized" value */
13490 
13491   /* store if player is automatically moved to next field */
13492   player->is_auto_moving = (player->programmed_action != MV_NONE);
13493 
13494   /* remove the last programmed player action */
13495   player->programmed_action = 0;
13496 
13497   if (player->MovPos)
13498   {
13499     /* should only happen if pre-1.2 tape recordings are played */
13500     /* this is only for backward compatibility */
13501 
13502     int original_move_delay_value = player->move_delay_value;
13503 
13504 #if DEBUG
13505     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
13506 	   tape.counter);
13507 #endif
13508 
13509     /* scroll remaining steps with finest movement resolution */
13510     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13511 
13512     while (player->MovPos)
13513     {
13514       ScrollPlayer(player, SCROLL_GO_ON);
13515       ScrollScreen(NULL, SCROLL_GO_ON);
13516 
13517       AdvanceFrameAndPlayerCounters(player->index_nr);
13518 
13519       DrawAllPlayers();
13520       BackToFront();
13521     }
13522 
13523     player->move_delay_value = original_move_delay_value;
13524   }
13525 
13526   player->is_active = FALSE;
13527 
13528   if (player->last_move_dir & MV_HORIZONTAL)
13529   {
13530     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13531       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13532   }
13533   else
13534   {
13535     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13536       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13537   }
13538 
13539 #if USE_FIXED_BORDER_RUNNING_GFX
13540   if (!moved && !player->is_active)
13541   {
13542     player->is_moving = FALSE;
13543     player->is_digging = FALSE;
13544     player->is_collecting = FALSE;
13545     player->is_snapping = FALSE;
13546     player->is_pushing = FALSE;
13547   }
13548 #endif
13549 
13550   jx = player->jx;
13551   jy = player->jy;
13552 
13553 #if 1
13554   if (moved & MP_MOVING && !ScreenMovPos &&
13555       (player->index_nr == game.centered_player_nr ||
13556        game.centered_player_nr == -1))
13557 #else
13558   if (moved & MP_MOVING && !ScreenMovPos &&
13559       (player == local_player || !options.network))
13560 #endif
13561   {
13562     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13563     int offset = game.scroll_delay_value;
13564 
13565     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13566     {
13567       /* actual player has left the screen -- scroll in that direction */
13568       if (jx != old_jx)		/* player has moved horizontally */
13569 	scroll_x += (jx - old_jx);
13570       else			/* player has moved vertically */
13571 	scroll_y += (jy - old_jy);
13572     }
13573     else
13574     {
13575       if (jx != old_jx)		/* player has moved horizontally */
13576       {
13577  	if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13578 	    (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13579 	  scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13580 
13581 	/* don't scroll over playfield boundaries */
13582 	if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13583 	  scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13584 
13585 	/* don't scroll more than one field at a time */
13586 	scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13587 
13588 	/* don't scroll against the player's moving direction */
13589 	if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13590 	    (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13591 	  scroll_x = old_scroll_x;
13592       }
13593       else			/* player has moved vertically */
13594       {
13595 	if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13596 	    (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13597 	  scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13598 
13599 	/* don't scroll over playfield boundaries */
13600 	if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13601 	  scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13602 
13603 	/* don't scroll more than one field at a time */
13604 	scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13605 
13606 	/* don't scroll against the player's moving direction */
13607 	if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13608 	    (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13609 	  scroll_y = old_scroll_y;
13610       }
13611     }
13612 
13613     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13614     {
13615 #if 1
13616       if (!options.network && game.centered_player_nr == -1 &&
13617 	  !AllPlayersInVisibleScreen())
13618       {
13619 	scroll_x = old_scroll_x;
13620 	scroll_y = old_scroll_y;
13621       }
13622       else
13623 #else
13624       if (!options.network && !AllPlayersInVisibleScreen())
13625       {
13626 	scroll_x = old_scroll_x;
13627 	scroll_y = old_scroll_y;
13628       }
13629       else
13630 #endif
13631       {
13632 	ScrollScreen(player, SCROLL_INIT);
13633 	ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13634       }
13635     }
13636   }
13637 
13638   player->StepFrame = 0;
13639 
13640   if (moved & MP_MOVING)
13641   {
13642     if (old_jx != jx && old_jy == jy)
13643       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13644     else if (old_jx == jx && old_jy != jy)
13645       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13646 
13647     TEST_DrawLevelField(jx, jy);	/* for "crumbled sand" */
13648 
13649     player->last_move_dir = player->MovDir;
13650     player->is_moving = TRUE;
13651     player->is_snapping = FALSE;
13652     player->is_switching = FALSE;
13653     player->is_dropping = FALSE;
13654     player->is_dropping_pressed = FALSE;
13655     player->drop_pressed_delay = 0;
13656 
13657 #if 0
13658     /* should better be called here than above, but this breaks some tapes */
13659     ScrollPlayer(player, SCROLL_INIT);
13660 #endif
13661   }
13662   else
13663   {
13664     CheckGravityMovementWhenNotMoving(player);
13665 
13666     player->is_moving = FALSE;
13667 
13668     /* at this point, the player is allowed to move, but cannot move right now
13669        (e.g. because of something blocking the way) -- ensure that the player
13670        is also allowed to move in the next frame (in old versions before 3.1.1,
13671        the player was forced to wait again for eight frames before next try) */
13672 
13673     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13674       player->move_delay = 0;	/* allow direct movement in the next frame */
13675   }
13676 
13677   if (player->move_delay == -1)		/* not yet initialized by DigField() */
13678     player->move_delay = player->move_delay_value;
13679 
13680   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13681   {
13682     TestIfPlayerTouchesBadThing(jx, jy);
13683     TestIfPlayerTouchesCustomElement(jx, jy);
13684   }
13685 
13686   if (!player->active)
13687     RemovePlayer(player);
13688 
13689   return moved;
13690 }
13691 
ScrollPlayer(struct PlayerInfo * player,int mode)13692 void ScrollPlayer(struct PlayerInfo *player, int mode)
13693 {
13694   int jx = player->jx, jy = player->jy;
13695   int last_jx = player->last_jx, last_jy = player->last_jy;
13696   int move_stepsize = TILEX / player->move_delay_value;
13697 
13698 #if USE_NEW_PLAYER_SPEED
13699   if (!player->active)
13700     return;
13701 
13702   if (player->MovPos == 0 && mode == SCROLL_GO_ON)	/* player not moving */
13703     return;
13704 #else
13705   if (!player->active || player->MovPos == 0)
13706     return;
13707 #endif
13708 
13709   if (mode == SCROLL_INIT)
13710   {
13711     player->actual_frame_counter = FrameCounter;
13712     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13713 
13714     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13715 	Feld[last_jx][last_jy] == EL_EMPTY)
13716     {
13717       int last_field_block_delay = 0;	/* start with no blocking at all */
13718       int block_delay_adjustment = player->block_delay_adjustment;
13719 
13720       /* if player blocks last field, add delay for exactly one move */
13721       if (player->block_last_field)
13722       {
13723 	last_field_block_delay += player->move_delay_value;
13724 
13725 	/* when blocking enabled, prevent moving up despite gravity */
13726 #if USE_PLAYER_GRAVITY
13727 	if (player->gravity && player->MovDir == MV_UP)
13728 	  block_delay_adjustment = -1;
13729 #else
13730 	if (game.gravity && player->MovDir == MV_UP)
13731 	  block_delay_adjustment = -1;
13732 #endif
13733       }
13734 
13735       /* add block delay adjustment (also possible when not blocking) */
13736       last_field_block_delay += block_delay_adjustment;
13737 
13738       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13739       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13740     }
13741 
13742 #if USE_NEW_PLAYER_SPEED
13743     if (player->MovPos != 0)	/* player has not yet reached destination */
13744       return;
13745 #else
13746     return;
13747 #endif
13748   }
13749   else if (!FrameReached(&player->actual_frame_counter, 1))
13750     return;
13751 
13752 #if USE_NEW_PLAYER_SPEED
13753   if (player->MovPos != 0)
13754   {
13755     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13756     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13757 
13758     /* before DrawPlayer() to draw correct player graphic for this case */
13759     if (player->MovPos == 0)
13760       CheckGravityMovement(player);
13761   }
13762 #else
13763   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13764   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13765 
13766   /* before DrawPlayer() to draw correct player graphic for this case */
13767   if (player->MovPos == 0)
13768     CheckGravityMovement(player);
13769 #endif
13770 
13771   if (player->MovPos == 0)	/* player reached destination field */
13772   {
13773     if (player->move_delay_reset_counter > 0)
13774     {
13775       player->move_delay_reset_counter--;
13776 
13777       if (player->move_delay_reset_counter == 0)
13778       {
13779 	/* continue with normal speed after quickly moving through gate */
13780 	HALVE_PLAYER_SPEED(player);
13781 
13782 	/* be able to make the next move without delay */
13783 	player->move_delay = 0;
13784       }
13785     }
13786 
13787     player->last_jx = jx;
13788     player->last_jy = jy;
13789 
13790     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13791 	Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13792 #if 1
13793 	Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13794 #endif
13795 	Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13796 	Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13797 #if 1
13798 	Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13799 #endif
13800 	Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13801 	Feld[jx][jy] == EL_SP_EXIT_OPENING)	/* <-- special case */
13802     {
13803       DrawPlayer(player);	/* needed here only to cleanup last field */
13804       RemovePlayer(player);
13805 
13806       if (local_player->friends_still_needed == 0 ||
13807 	  IS_SP_ELEMENT(Feld[jx][jy]))
13808 	PlayerWins(player);
13809     }
13810 
13811     /* this breaks one level: "machine", level 000 */
13812     {
13813       int move_direction = player->MovDir;
13814       int enter_side = MV_DIR_OPPOSITE(move_direction);
13815       int leave_side = move_direction;
13816       int old_jx = last_jx;
13817       int old_jy = last_jy;
13818       int old_element = Feld[old_jx][old_jy];
13819       int new_element = Feld[jx][jy];
13820 
13821       if (IS_CUSTOM_ELEMENT(old_element))
13822 	CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13823 				   CE_LEFT_BY_PLAYER,
13824 				   player->index_bit, leave_side);
13825 
13826       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13827 					  CE_PLAYER_LEAVES_X,
13828 					  player->index_bit, leave_side);
13829 
13830       if (IS_CUSTOM_ELEMENT(new_element))
13831 	CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13832 				   player->index_bit, enter_side);
13833 
13834       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13835 					  CE_PLAYER_ENTERS_X,
13836 					  player->index_bit, enter_side);
13837 
13838 #if USE_FIX_CE_ACTION_WITH_PLAYER
13839       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13840 					CE_MOVE_OF_X, move_direction);
13841 #else
13842       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13843 					CE_MOVE_OF_X, move_direction);
13844 #endif
13845     }
13846 
13847     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13848     {
13849       TestIfPlayerTouchesBadThing(jx, jy);
13850       TestIfPlayerTouchesCustomElement(jx, jy);
13851 
13852       /* needed because pushed element has not yet reached its destination,
13853 	 so it would trigger a change event at its previous field location */
13854       if (!player->is_pushing)
13855 	TestIfElementTouchesCustomElement(jx, jy);	/* for empty space */
13856 
13857       if (!player->active)
13858 	RemovePlayer(player);
13859     }
13860 
13861     if (!local_player->LevelSolved && level.use_step_counter)
13862     {
13863       int i;
13864 
13865       TimePlayed++;
13866 
13867       if (TimeLeft > 0)
13868       {
13869 	TimeLeft--;
13870 
13871 	if (TimeLeft <= 10 && setup.time_limit)
13872 	  PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13873 
13874 #if 1
13875 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13876 
13877 	DisplayGameControlValues();
13878 #else
13879 	DrawGameValue_Time(TimeLeft);
13880 #endif
13881 
13882 	if (!TimeLeft && setup.time_limit)
13883 	  for (i = 0; i < MAX_PLAYERS; i++)
13884 	    KillPlayer(&stored_player[i]);
13885       }
13886 #if 1
13887       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13888       {
13889 	game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13890 
13891 	DisplayGameControlValues();
13892       }
13893 #else
13894       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
13895 	DrawGameValue_Time(TimePlayed);
13896 #endif
13897     }
13898 
13899     if (tape.single_step && tape.recording && !tape.pausing &&
13900 	!player->programmed_action)
13901       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13902   }
13903 }
13904 
ScrollScreen(struct PlayerInfo * player,int mode)13905 void ScrollScreen(struct PlayerInfo *player, int mode)
13906 {
13907   static unsigned int screen_frame_counter = 0;
13908 
13909   if (mode == SCROLL_INIT)
13910   {
13911     /* set scrolling step size according to actual player's moving speed */
13912     ScrollStepSize = TILEX / player->move_delay_value;
13913 
13914     screen_frame_counter = FrameCounter;
13915     ScreenMovDir = player->MovDir;
13916     ScreenMovPos = player->MovPos;
13917     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13918     return;
13919   }
13920   else if (!FrameReached(&screen_frame_counter, 1))
13921     return;
13922 
13923   if (ScreenMovPos)
13924   {
13925     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13926     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13927     redraw_mask |= REDRAW_FIELD;
13928   }
13929   else
13930     ScreenMovDir = MV_NONE;
13931 }
13932 
TestIfPlayerTouchesCustomElement(int x,int y)13933 void TestIfPlayerTouchesCustomElement(int x, int y)
13934 {
13935   static int xy[4][2] =
13936   {
13937     { 0, -1 },
13938     { -1, 0 },
13939     { +1, 0 },
13940     { 0, +1 }
13941   };
13942   static int trigger_sides[4][2] =
13943   {
13944     /* center side       border side */
13945     { CH_SIDE_TOP,	CH_SIDE_BOTTOM	},	/* check top    */
13946     { CH_SIDE_LEFT,	CH_SIDE_RIGHT	},	/* check left   */
13947     { CH_SIDE_RIGHT,	CH_SIDE_LEFT	},	/* check right  */
13948     { CH_SIDE_BOTTOM,	CH_SIDE_TOP	}	/* check bottom */
13949   };
13950   static int touch_dir[4] =
13951   {
13952     MV_LEFT | MV_RIGHT,
13953     MV_UP   | MV_DOWN,
13954     MV_UP   | MV_DOWN,
13955     MV_LEFT | MV_RIGHT
13956   };
13957   int center_element = Feld[x][y];	/* should always be non-moving! */
13958   int i;
13959 
13960   for (i = 0; i < NUM_DIRECTIONS; i++)
13961   {
13962     int xx = x + xy[i][0];
13963     int yy = y + xy[i][1];
13964     int center_side = trigger_sides[i][0];
13965     int border_side = trigger_sides[i][1];
13966     int border_element;
13967 
13968     if (!IN_LEV_FIELD(xx, yy))
13969       continue;
13970 
13971     if (IS_PLAYER(x, y))		/* player found at center element */
13972     {
13973       struct PlayerInfo *player = PLAYERINFO(x, y);
13974 
13975       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13976 	border_element = Feld[xx][yy];		/* may be moving! */
13977       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13978 	border_element = Feld[xx][yy];
13979       else if (MovDir[xx][yy] & touch_dir[i])	/* elements are touching */
13980 	border_element = MovingOrBlocked2Element(xx, yy);
13981       else
13982 	continue;		/* center and border element do not touch */
13983 
13984       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13985 				 player->index_bit, border_side);
13986       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13987 					  CE_PLAYER_TOUCHES_X,
13988 					  player->index_bit, border_side);
13989 
13990 #if USE_FIX_CE_ACTION_WITH_PLAYER
13991       {
13992 	/* use player element that is initially defined in the level playfield,
13993 	   not the player element that corresponds to the runtime player number
13994 	   (example: a level that contains EL_PLAYER_3 as the only player would
13995 	   incorrectly give EL_PLAYER_1 for "player->element_nr") */
13996 	int player_element = PLAYERINFO(x, y)->initial_element;
13997 
13998 	CheckElementChangeBySide(xx, yy, border_element, player_element,
13999 				 CE_TOUCHING_X, border_side);
14000       }
14001 #endif
14002     }
14003     else if (IS_PLAYER(xx, yy))		/* player found at border element */
14004     {
14005       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14006 
14007       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14008       {
14009 	if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14010 	  continue;		/* center and border element do not touch */
14011       }
14012 
14013       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14014 				 player->index_bit, center_side);
14015       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14016 					  CE_PLAYER_TOUCHES_X,
14017 					  player->index_bit, center_side);
14018 
14019 #if USE_FIX_CE_ACTION_WITH_PLAYER
14020       {
14021 	/* use player element that is initially defined in the level playfield,
14022 	   not the player element that corresponds to the runtime player number
14023 	   (example: a level that contains EL_PLAYER_3 as the only player would
14024 	   incorrectly give EL_PLAYER_1 for "player->element_nr") */
14025 	int player_element = PLAYERINFO(xx, yy)->initial_element;
14026 
14027 	CheckElementChangeBySide(x, y, center_element, player_element,
14028 				 CE_TOUCHING_X, center_side);
14029       }
14030 #endif
14031 
14032       break;
14033     }
14034   }
14035 }
14036 
14037 #if USE_ELEMENT_TOUCHING_BUGFIX
14038 
TestIfElementTouchesCustomElement(int x,int y)14039 void TestIfElementTouchesCustomElement(int x, int y)
14040 {
14041   static int xy[4][2] =
14042   {
14043     { 0, -1 },
14044     { -1, 0 },
14045     { +1, 0 },
14046     { 0, +1 }
14047   };
14048   static int trigger_sides[4][2] =
14049   {
14050     /* center side	border side */
14051     { CH_SIDE_TOP,	CH_SIDE_BOTTOM	},	/* check top    */
14052     { CH_SIDE_LEFT,	CH_SIDE_RIGHT	},	/* check left   */
14053     { CH_SIDE_RIGHT,	CH_SIDE_LEFT	},	/* check right  */
14054     { CH_SIDE_BOTTOM,	CH_SIDE_TOP	}	/* check bottom */
14055   };
14056   static int touch_dir[4] =
14057   {
14058     MV_LEFT | MV_RIGHT,
14059     MV_UP   | MV_DOWN,
14060     MV_UP   | MV_DOWN,
14061     MV_LEFT | MV_RIGHT
14062   };
14063   boolean change_center_element = FALSE;
14064   int center_element = Feld[x][y];	/* should always be non-moving! */
14065   int border_element_old[NUM_DIRECTIONS];
14066   int i;
14067 
14068   for (i = 0; i < NUM_DIRECTIONS; i++)
14069   {
14070     int xx = x + xy[i][0];
14071     int yy = y + xy[i][1];
14072     int border_element;
14073 
14074     border_element_old[i] = -1;
14075 
14076     if (!IN_LEV_FIELD(xx, yy))
14077       continue;
14078 
14079     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14080       border_element = Feld[xx][yy];	/* may be moving! */
14081     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14082       border_element = Feld[xx][yy];
14083     else if (MovDir[xx][yy] & touch_dir[i])	/* elements are touching */
14084       border_element = MovingOrBlocked2Element(xx, yy);
14085     else
14086       continue;			/* center and border element do not touch */
14087 
14088     border_element_old[i] = border_element;
14089   }
14090 
14091   for (i = 0; i < NUM_DIRECTIONS; i++)
14092   {
14093     int xx = x + xy[i][0];
14094     int yy = y + xy[i][1];
14095     int center_side = trigger_sides[i][0];
14096     int border_element = border_element_old[i];
14097 
14098     if (border_element == -1)
14099       continue;
14100 
14101     /* check for change of border element */
14102     CheckElementChangeBySide(xx, yy, border_element, center_element,
14103 			     CE_TOUCHING_X, center_side);
14104 
14105     /* (center element cannot be player, so we dont have to check this here) */
14106   }
14107 
14108   for (i = 0; i < NUM_DIRECTIONS; i++)
14109   {
14110     int xx = x + xy[i][0];
14111     int yy = y + xy[i][1];
14112     int border_side = trigger_sides[i][1];
14113     int border_element = border_element_old[i];
14114 
14115     if (border_element == -1)
14116       continue;
14117 
14118     /* check for change of center element (but change it only once) */
14119     if (!change_center_element)
14120       change_center_element =
14121 	CheckElementChangeBySide(x, y, center_element, border_element,
14122 				 CE_TOUCHING_X, border_side);
14123 
14124 #if USE_FIX_CE_ACTION_WITH_PLAYER
14125     if (IS_PLAYER(xx, yy))
14126     {
14127       /* use player element that is initially defined in the level playfield,
14128 	 not the player element that corresponds to the runtime player number
14129 	 (example: a level that contains EL_PLAYER_3 as the only player would
14130 	 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14131       int player_element = PLAYERINFO(xx, yy)->initial_element;
14132 
14133       CheckElementChangeBySide(x, y, center_element, player_element,
14134 			       CE_TOUCHING_X, border_side);
14135     }
14136 #endif
14137   }
14138 }
14139 
14140 #else
14141 
TestIfElementTouchesCustomElement_OLD(int x,int y)14142 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14143 {
14144   static int xy[4][2] =
14145   {
14146     { 0, -1 },
14147     { -1, 0 },
14148     { +1, 0 },
14149     { 0, +1 }
14150   };
14151   static int trigger_sides[4][2] =
14152   {
14153     /* center side	border side */
14154     { CH_SIDE_TOP,	CH_SIDE_BOTTOM	},	/* check top    */
14155     { CH_SIDE_LEFT,	CH_SIDE_RIGHT	},	/* check left   */
14156     { CH_SIDE_RIGHT,	CH_SIDE_LEFT	},	/* check right  */
14157     { CH_SIDE_BOTTOM,	CH_SIDE_TOP	}	/* check bottom */
14158   };
14159   static int touch_dir[4] =
14160   {
14161     MV_LEFT | MV_RIGHT,
14162     MV_UP   | MV_DOWN,
14163     MV_UP   | MV_DOWN,
14164     MV_LEFT | MV_RIGHT
14165   };
14166   boolean change_center_element = FALSE;
14167   int center_element = Feld[x][y];	/* should always be non-moving! */
14168   int i;
14169 
14170   for (i = 0; i < NUM_DIRECTIONS; i++)
14171   {
14172     int xx = x + xy[i][0];
14173     int yy = y + xy[i][1];
14174     int center_side = trigger_sides[i][0];
14175     int border_side = trigger_sides[i][1];
14176     int border_element;
14177 
14178     if (!IN_LEV_FIELD(xx, yy))
14179       continue;
14180 
14181     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14182       border_element = Feld[xx][yy];	/* may be moving! */
14183     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14184       border_element = Feld[xx][yy];
14185     else if (MovDir[xx][yy] & touch_dir[i])	/* elements are touching */
14186       border_element = MovingOrBlocked2Element(xx, yy);
14187     else
14188       continue;			/* center and border element do not touch */
14189 
14190     /* check for change of center element (but change it only once) */
14191     if (!change_center_element)
14192       change_center_element =
14193 	CheckElementChangeBySide(x, y, center_element, border_element,
14194 				 CE_TOUCHING_X, border_side);
14195 
14196     /* check for change of border element */
14197     CheckElementChangeBySide(xx, yy, border_element, center_element,
14198 			     CE_TOUCHING_X, center_side);
14199   }
14200 }
14201 
14202 #endif
14203 
TestIfElementHitsCustomElement(int x,int y,int direction)14204 void TestIfElementHitsCustomElement(int x, int y, int direction)
14205 {
14206   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14207   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14208   int hitx = x + dx, hity = y + dy;
14209   int hitting_element = Feld[x][y];
14210   int touched_element;
14211 
14212   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14213     return;
14214 
14215   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14216 		     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14217 
14218   if (IN_LEV_FIELD(hitx, hity))
14219   {
14220     int opposite_direction = MV_DIR_OPPOSITE(direction);
14221     int hitting_side = direction;
14222     int touched_side = opposite_direction;
14223     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14224 			  MovDir[hitx][hity] != direction ||
14225 			  ABS(MovPos[hitx][hity]) <= TILEY / 2);
14226 
14227     object_hit = TRUE;
14228 
14229     if (object_hit)
14230     {
14231       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14232 			       CE_HITTING_X, touched_side);
14233 
14234       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14235 			       CE_HIT_BY_X, hitting_side);
14236 
14237       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14238 			       CE_HIT_BY_SOMETHING, opposite_direction);
14239 
14240 #if USE_FIX_CE_ACTION_WITH_PLAYER
14241       if (IS_PLAYER(hitx, hity))
14242       {
14243 	/* use player element that is initially defined in the level playfield,
14244 	   not the player element that corresponds to the runtime player number
14245 	   (example: a level that contains EL_PLAYER_3 as the only player would
14246 	   incorrectly give EL_PLAYER_1 for "player->element_nr") */
14247 	int player_element = PLAYERINFO(hitx, hity)->initial_element;
14248 
14249 	CheckElementChangeBySide(x, y, hitting_element, player_element,
14250 				 CE_HITTING_X, touched_side);
14251       }
14252 #endif
14253     }
14254   }
14255 
14256   /* "hitting something" is also true when hitting the playfield border */
14257   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14258 			   CE_HITTING_SOMETHING, direction);
14259 }
14260 
14261 #if 0
14262 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14263 {
14264   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14265   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14266   int hitx = x + dx, hity = y + dy;
14267   int hitting_element = Feld[x][y];
14268   int touched_element;
14269 #if 0
14270   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14271 			!IS_FREE(hitx, hity) &&
14272 			(!IS_MOVING(hitx, hity) ||
14273 			 MovDir[hitx][hity] != direction ||
14274 			 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14275 #endif
14276 
14277   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14278     return;
14279 
14280 #if 0
14281   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14282     return;
14283 #endif
14284 
14285   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14286 		     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14287 
14288   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14289 			   EP_CAN_SMASH_EVERYTHING, direction);
14290 
14291   if (IN_LEV_FIELD(hitx, hity))
14292   {
14293     int opposite_direction = MV_DIR_OPPOSITE(direction);
14294     int hitting_side = direction;
14295     int touched_side = opposite_direction;
14296 #if 0
14297     int touched_element = MovingOrBlocked2Element(hitx, hity);
14298 #endif
14299 #if 1
14300     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14301 			  MovDir[hitx][hity] != direction ||
14302 			  ABS(MovPos[hitx][hity]) <= TILEY / 2);
14303 
14304     object_hit = TRUE;
14305 #endif
14306 
14307     if (object_hit)
14308     {
14309       int i;
14310 
14311       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14312 			       CE_SMASHED_BY_SOMETHING, opposite_direction);
14313 
14314       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14315 			       CE_OTHER_IS_SMASHING, touched_side);
14316 
14317       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14318 			       CE_OTHER_GETS_SMASHED, hitting_side);
14319     }
14320   }
14321 }
14322 #endif
14323 
TestIfGoodThingHitsBadThing(int good_x,int good_y,int good_move_dir)14324 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14325 {
14326   int i, kill_x = -1, kill_y = -1;
14327 
14328   int bad_element = -1;
14329   static int test_xy[4][2] =
14330   {
14331     { 0, -1 },
14332     { -1, 0 },
14333     { +1, 0 },
14334     { 0, +1 }
14335   };
14336   static int test_dir[4] =
14337   {
14338     MV_UP,
14339     MV_LEFT,
14340     MV_RIGHT,
14341     MV_DOWN
14342   };
14343 
14344   for (i = 0; i < NUM_DIRECTIONS; i++)
14345   {
14346     int test_x, test_y, test_move_dir, test_element;
14347 
14348     test_x = good_x + test_xy[i][0];
14349     test_y = good_y + test_xy[i][1];
14350 
14351     if (!IN_LEV_FIELD(test_x, test_y))
14352       continue;
14353 
14354     test_move_dir =
14355       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14356 
14357     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14358 
14359     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14360        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14361     */
14362     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14363 	(DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14364     {
14365       kill_x = test_x;
14366       kill_y = test_y;
14367       bad_element = test_element;
14368 
14369       break;
14370     }
14371   }
14372 
14373   if (kill_x != -1 || kill_y != -1)
14374   {
14375     if (IS_PLAYER(good_x, good_y))
14376     {
14377       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14378 
14379       if (player->shield_deadly_time_left > 0 &&
14380 	  !IS_INDESTRUCTIBLE(bad_element))
14381 	Bang(kill_x, kill_y);
14382       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14383 	KillPlayer(player);
14384     }
14385     else
14386       Bang(good_x, good_y);
14387   }
14388 }
14389 
TestIfBadThingHitsGoodThing(int bad_x,int bad_y,int bad_move_dir)14390 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14391 {
14392   int i, kill_x = -1, kill_y = -1;
14393   int bad_element = Feld[bad_x][bad_y];
14394   static int test_xy[4][2] =
14395   {
14396     { 0, -1 },
14397     { -1, 0 },
14398     { +1, 0 },
14399     { 0, +1 }
14400   };
14401   static int touch_dir[4] =
14402   {
14403     MV_LEFT | MV_RIGHT,
14404     MV_UP   | MV_DOWN,
14405     MV_UP   | MV_DOWN,
14406     MV_LEFT | MV_RIGHT
14407   };
14408   static int test_dir[4] =
14409   {
14410     MV_UP,
14411     MV_LEFT,
14412     MV_RIGHT,
14413     MV_DOWN
14414   };
14415 
14416   if (bad_element == EL_EXPLOSION)	/* skip just exploding bad things */
14417     return;
14418 
14419   for (i = 0; i < NUM_DIRECTIONS; i++)
14420   {
14421     int test_x, test_y, test_move_dir, test_element;
14422 
14423     test_x = bad_x + test_xy[i][0];
14424     test_y = bad_y + test_xy[i][1];
14425 
14426     if (!IN_LEV_FIELD(test_x, test_y))
14427       continue;
14428 
14429     test_move_dir =
14430       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14431 
14432     test_element = Feld[test_x][test_y];
14433 
14434     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14435        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14436     */
14437     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14438 	(DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14439     {
14440       /* good thing is player or penguin that does not move away */
14441       if (IS_PLAYER(test_x, test_y))
14442       {
14443 	struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14444 
14445 	if (bad_element == EL_ROBOT && player->is_moving)
14446 	  continue;	/* robot does not kill player if he is moving */
14447 
14448 	if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14449 	{
14450 	  if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14451 	    continue;		/* center and border element do not touch */
14452 	}
14453 
14454 	kill_x = test_x;
14455 	kill_y = test_y;
14456 
14457 	break;
14458       }
14459       else if (test_element == EL_PENGUIN)
14460       {
14461 	kill_x = test_x;
14462 	kill_y = test_y;
14463 
14464 	break;
14465       }
14466     }
14467   }
14468 
14469   if (kill_x != -1 || kill_y != -1)
14470   {
14471     if (IS_PLAYER(kill_x, kill_y))
14472     {
14473       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14474 
14475       if (player->shield_deadly_time_left > 0 &&
14476 	  !IS_INDESTRUCTIBLE(bad_element))
14477 	Bang(bad_x, bad_y);
14478       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14479 	KillPlayer(player);
14480     }
14481     else
14482       Bang(kill_x, kill_y);
14483   }
14484 }
14485 
TestIfGoodThingGetsHitByBadThing(int bad_x,int bad_y,int bad_move_dir)14486 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14487 {
14488   int bad_element = Feld[bad_x][bad_y];
14489   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14490   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14491   int test_x = bad_x + dx, test_y = bad_y + dy;
14492   int test_move_dir, test_element;
14493   int kill_x = -1, kill_y = -1;
14494 
14495   if (!IN_LEV_FIELD(test_x, test_y))
14496     return;
14497 
14498   test_move_dir =
14499     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14500 
14501   test_element = Feld[test_x][test_y];
14502 
14503   if (test_move_dir != bad_move_dir)
14504   {
14505     /* good thing can be player or penguin that does not move away */
14506     if (IS_PLAYER(test_x, test_y))
14507     {
14508       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14509 
14510       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14511 	 player as being hit when he is moving towards the bad thing, because
14512 	 the "get hit by" condition would be lost after the player stops) */
14513       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14514 	return;		/* player moves away from bad thing */
14515 
14516       kill_x = test_x;
14517       kill_y = test_y;
14518     }
14519     else if (test_element == EL_PENGUIN)
14520     {
14521       kill_x = test_x;
14522       kill_y = test_y;
14523     }
14524   }
14525 
14526   if (kill_x != -1 || kill_y != -1)
14527   {
14528     if (IS_PLAYER(kill_x, kill_y))
14529     {
14530       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14531 
14532       if (player->shield_deadly_time_left > 0 &&
14533 	  !IS_INDESTRUCTIBLE(bad_element))
14534 	Bang(bad_x, bad_y);
14535       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14536 	KillPlayer(player);
14537     }
14538     else
14539       Bang(kill_x, kill_y);
14540   }
14541 }
14542 
TestIfPlayerTouchesBadThing(int x,int y)14543 void TestIfPlayerTouchesBadThing(int x, int y)
14544 {
14545   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14546 }
14547 
TestIfPlayerRunsIntoBadThing(int x,int y,int move_dir)14548 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14549 {
14550   TestIfGoodThingHitsBadThing(x, y, move_dir);
14551 }
14552 
TestIfBadThingTouchesPlayer(int x,int y)14553 void TestIfBadThingTouchesPlayer(int x, int y)
14554 {
14555   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14556 }
14557 
TestIfBadThingRunsIntoPlayer(int x,int y,int move_dir)14558 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14559 {
14560   TestIfBadThingHitsGoodThing(x, y, move_dir);
14561 }
14562 
TestIfFriendTouchesBadThing(int x,int y)14563 void TestIfFriendTouchesBadThing(int x, int y)
14564 {
14565   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14566 }
14567 
TestIfBadThingTouchesFriend(int x,int y)14568 void TestIfBadThingTouchesFriend(int x, int y)
14569 {
14570   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14571 }
14572 
TestIfBadThingTouchesOtherBadThing(int bad_x,int bad_y)14573 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14574 {
14575   int i, kill_x = bad_x, kill_y = bad_y;
14576   static int xy[4][2] =
14577   {
14578     { 0, -1 },
14579     { -1, 0 },
14580     { +1, 0 },
14581     { 0, +1 }
14582   };
14583 
14584   for (i = 0; i < NUM_DIRECTIONS; i++)
14585   {
14586     int x, y, element;
14587 
14588     x = bad_x + xy[i][0];
14589     y = bad_y + xy[i][1];
14590     if (!IN_LEV_FIELD(x, y))
14591       continue;
14592 
14593     element = Feld[x][y];
14594     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14595 	element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14596     {
14597       kill_x = x;
14598       kill_y = y;
14599       break;
14600     }
14601   }
14602 
14603   if (kill_x != bad_x || kill_y != bad_y)
14604     Bang(bad_x, bad_y);
14605 }
14606 
KillPlayer(struct PlayerInfo * player)14607 void KillPlayer(struct PlayerInfo *player)
14608 {
14609   int jx = player->jx, jy = player->jy;
14610 
14611   if (!player->active)
14612     return;
14613 
14614 #if 0
14615   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14616 	 player->killed, player->active, player->reanimated);
14617 #endif
14618 
14619   /* the following code was introduced to prevent an infinite loop when calling
14620      -> Bang()
14621      -> CheckTriggeredElementChangeExt()
14622      -> ExecuteCustomElementAction()
14623      -> KillPlayer()
14624      -> (infinitely repeating the above sequence of function calls)
14625      which occurs when killing the player while having a CE with the setting
14626      "kill player X when explosion of <player X>"; the solution using a new
14627      field "player->killed" was chosen for backwards compatibility, although
14628      clever use of the fields "player->active" etc. would probably also work */
14629 #if 1
14630   if (player->killed)
14631     return;
14632 #endif
14633 
14634   player->killed = TRUE;
14635 
14636   /* remove accessible field at the player's position */
14637   Feld[jx][jy] = EL_EMPTY;
14638 
14639   /* deactivate shield (else Bang()/Explode() would not work right) */
14640   player->shield_normal_time_left = 0;
14641   player->shield_deadly_time_left = 0;
14642 
14643 #if 0
14644   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14645 	 player->killed, player->active, player->reanimated);
14646 #endif
14647 
14648   Bang(jx, jy);
14649 
14650 #if 0
14651   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14652 	 player->killed, player->active, player->reanimated);
14653 #endif
14654 
14655 #if USE_PLAYER_REANIMATION
14656 #if 1
14657   if (player->reanimated)	/* killed player may have been reanimated */
14658     player->killed = player->reanimated = FALSE;
14659   else
14660     BuryPlayer(player);
14661 #else
14662   if (player->killed)		/* player may have been reanimated */
14663     BuryPlayer(player);
14664 #endif
14665 #else
14666   BuryPlayer(player);
14667 #endif
14668 }
14669 
KillPlayerUnlessEnemyProtected(int x,int y)14670 static void KillPlayerUnlessEnemyProtected(int x, int y)
14671 {
14672   if (!PLAYER_ENEMY_PROTECTED(x, y))
14673     KillPlayer(PLAYERINFO(x, y));
14674 }
14675 
KillPlayerUnlessExplosionProtected(int x,int y)14676 static void KillPlayerUnlessExplosionProtected(int x, int y)
14677 {
14678   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14679     KillPlayer(PLAYERINFO(x, y));
14680 }
14681 
BuryPlayer(struct PlayerInfo * player)14682 void BuryPlayer(struct PlayerInfo *player)
14683 {
14684   int jx = player->jx, jy = player->jy;
14685 
14686   if (!player->active)
14687     return;
14688 
14689   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14690   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14691 
14692   player->GameOver = TRUE;
14693   RemovePlayer(player);
14694 }
14695 
RemovePlayer(struct PlayerInfo * player)14696 void RemovePlayer(struct PlayerInfo *player)
14697 {
14698   int jx = player->jx, jy = player->jy;
14699   int i, found = FALSE;
14700 
14701   player->present = FALSE;
14702   player->active = FALSE;
14703 
14704   if (!ExplodeField[jx][jy])
14705     StorePlayer[jx][jy] = 0;
14706 
14707   if (player->is_moving)
14708     TEST_DrawLevelField(player->last_jx, player->last_jy);
14709 
14710   for (i = 0; i < MAX_PLAYERS; i++)
14711     if (stored_player[i].active)
14712       found = TRUE;
14713 
14714   if (!found)
14715     AllPlayersGone = TRUE;
14716 
14717   ExitX = ZX = jx;
14718   ExitY = ZY = jy;
14719 }
14720 
14721 #if USE_NEW_SNAP_DELAY
setFieldForSnapping(int x,int y,int element,int direction)14722 static void setFieldForSnapping(int x, int y, int element, int direction)
14723 {
14724   struct ElementInfo *ei = &element_info[element];
14725   int direction_bit = MV_DIR_TO_BIT(direction);
14726   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14727   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14728 		IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14729 
14730   Feld[x][y] = EL_ELEMENT_SNAPPING;
14731   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14732 
14733   ResetGfxAnimation(x, y);
14734 
14735   GfxElement[x][y] = element;
14736   GfxAction[x][y] = action;
14737   GfxDir[x][y] = direction;
14738   GfxFrame[x][y] = -1;
14739 }
14740 #endif
14741 
14742 /*
14743   =============================================================================
14744   checkDiagonalPushing()
14745   -----------------------------------------------------------------------------
14746   check if diagonal input device direction results in pushing of object
14747   (by checking if the alternative direction is walkable, diggable, ...)
14748   =============================================================================
14749 */
14750 
checkDiagonalPushing(struct PlayerInfo * player,int x,int y,int real_dx,int real_dy)14751 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14752 				    int x, int y, int real_dx, int real_dy)
14753 {
14754   int jx, jy, dx, dy, xx, yy;
14755 
14756   if (real_dx == 0 || real_dy == 0)	/* no diagonal direction => push */
14757     return TRUE;
14758 
14759   /* diagonal direction: check alternative direction */
14760   jx = player->jx;
14761   jy = player->jy;
14762   dx = x - jx;
14763   dy = y - jy;
14764   xx = jx + (dx == 0 ? real_dx : 0);
14765   yy = jy + (dy == 0 ? real_dy : 0);
14766 
14767   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14768 }
14769 
14770 /*
14771   =============================================================================
14772   DigField()
14773   -----------------------------------------------------------------------------
14774   x, y:			field next to player (non-diagonal) to try to dig to
14775   real_dx, real_dy:	direction as read from input device (can be diagonal)
14776   =============================================================================
14777 */
14778 
DigField(struct PlayerInfo * player,int oldx,int oldy,int x,int y,int real_dx,int real_dy,int mode)14779 static int DigField(struct PlayerInfo *player,
14780 		    int oldx, int oldy, int x, int y,
14781 		    int real_dx, int real_dy, int mode)
14782 {
14783   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14784   boolean player_was_pushing = player->is_pushing;
14785   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14786   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14787   int jx = oldx, jy = oldy;
14788   int dx = x - jx, dy = y - jy;
14789   int nextx = x + dx, nexty = y + dy;
14790   int move_direction = (dx == -1 ? MV_LEFT  :
14791 			dx == +1 ? MV_RIGHT :
14792 			dy == -1 ? MV_UP    :
14793 			dy == +1 ? MV_DOWN  : MV_NONE);
14794   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14795   int dig_side = MV_DIR_OPPOSITE(move_direction);
14796   int old_element = Feld[jx][jy];
14797 #if USE_FIXED_DONT_RUN_INTO
14798   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14799 #else
14800   int element;
14801 #endif
14802   int collect_count;
14803 
14804   if (is_player)		/* function can also be called by EL_PENGUIN */
14805   {
14806     if (player->MovPos == 0)
14807     {
14808       player->is_digging = FALSE;
14809       player->is_collecting = FALSE;
14810     }
14811 
14812     if (player->MovPos == 0)	/* last pushing move finished */
14813       player->is_pushing = FALSE;
14814 
14815     if (mode == DF_NO_PUSH)	/* player just stopped pushing */
14816     {
14817       player->is_switching = FALSE;
14818       player->push_delay = -1;
14819 
14820       return MP_NO_ACTION;
14821     }
14822   }
14823 
14824 #if !USE_FIXED_DONT_RUN_INTO
14825   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14826     return MP_NO_ACTION;
14827 #endif
14828 
14829   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14830     old_element = Back[jx][jy];
14831 
14832   /* in case of element dropped at player position, check background */
14833   else if (Back[jx][jy] != EL_EMPTY &&
14834 	   game.engine_version >= VERSION_IDENT(2,2,0,0))
14835     old_element = Back[jx][jy];
14836 
14837   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14838     return MP_NO_ACTION;	/* field has no opening in this direction */
14839 
14840   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14841     return MP_NO_ACTION;	/* field has no opening in this direction */
14842 
14843 #if USE_FIXED_DONT_RUN_INTO
14844   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14845   {
14846     SplashAcid(x, y);
14847 
14848     Feld[jx][jy] = player->artwork_element;
14849     InitMovingField(jx, jy, MV_DOWN);
14850     Store[jx][jy] = EL_ACID;
14851     ContinueMoving(jx, jy);
14852     BuryPlayer(player);
14853 
14854     return MP_DONT_RUN_INTO;
14855   }
14856 #endif
14857 
14858 #if USE_FIXED_DONT_RUN_INTO
14859   if (player_can_move && DONT_RUN_INTO(element))
14860   {
14861     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14862 
14863     return MP_DONT_RUN_INTO;
14864   }
14865 #endif
14866 
14867 #if USE_FIXED_DONT_RUN_INTO
14868   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14869     return MP_NO_ACTION;
14870 #endif
14871 
14872 #if !USE_FIXED_DONT_RUN_INTO
14873   element = Feld[x][y];
14874 #endif
14875 
14876   collect_count = element_info[element].collect_count_initial;
14877 
14878   if (!is_player && !IS_COLLECTIBLE(element))	/* penguin cannot collect it */
14879     return MP_NO_ACTION;
14880 
14881   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14882     player_can_move = player_can_move_or_snap;
14883 
14884   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14885       game.engine_version >= VERSION_IDENT(2,2,0,0))
14886   {
14887     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14888 			       player->index_bit, dig_side);
14889     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14890 					player->index_bit, dig_side);
14891 
14892     if (element == EL_DC_LANDMINE)
14893       Bang(x, y);
14894 
14895     if (Feld[x][y] != element)		/* field changed by snapping */
14896       return MP_ACTION;
14897 
14898     return MP_NO_ACTION;
14899   }
14900 
14901 #if USE_PLAYER_GRAVITY
14902   if (player->gravity && is_player && !player->is_auto_moving &&
14903       canFallDown(player) && move_direction != MV_DOWN &&
14904       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14905     return MP_NO_ACTION;	/* player cannot walk here due to gravity */
14906 #else
14907   if (game.gravity && is_player && !player->is_auto_moving &&
14908       canFallDown(player) && move_direction != MV_DOWN &&
14909       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14910     return MP_NO_ACTION;	/* player cannot walk here due to gravity */
14911 #endif
14912 
14913   if (player_can_move &&
14914       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14915   {
14916     int sound_element = SND_ELEMENT(element);
14917     int sound_action = ACTION_WALKING;
14918 
14919     if (IS_RND_GATE(element))
14920     {
14921       if (!player->key[RND_GATE_NR(element)])
14922 	return MP_NO_ACTION;
14923     }
14924     else if (IS_RND_GATE_GRAY(element))
14925     {
14926       if (!player->key[RND_GATE_GRAY_NR(element)])
14927 	return MP_NO_ACTION;
14928     }
14929     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14930     {
14931       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14932 	return MP_NO_ACTION;
14933     }
14934     else if (element == EL_EXIT_OPEN ||
14935 	     element == EL_EM_EXIT_OPEN ||
14936 #if 1
14937 	     element == EL_EM_EXIT_OPENING ||
14938 #endif
14939 	     element == EL_STEEL_EXIT_OPEN ||
14940 	     element == EL_EM_STEEL_EXIT_OPEN ||
14941 #if 1
14942 	     element == EL_EM_STEEL_EXIT_OPENING ||
14943 #endif
14944 	     element == EL_SP_EXIT_OPEN ||
14945 	     element == EL_SP_EXIT_OPENING)
14946     {
14947       sound_action = ACTION_PASSING;	/* player is passing exit */
14948     }
14949     else if (element == EL_EMPTY)
14950     {
14951       sound_action = ACTION_MOVING;		/* nothing to walk on */
14952     }
14953 
14954     /* play sound from background or player, whatever is available */
14955     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14956       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14957     else
14958       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14959   }
14960   else if (player_can_move &&
14961 	   IS_PASSABLE(element) && canPassField(x, y, move_direction))
14962   {
14963     if (!ACCESS_FROM(element, opposite_direction))
14964       return MP_NO_ACTION;	/* field not accessible from this direction */
14965 
14966     if (CAN_MOVE(element))	/* only fixed elements can be passed! */
14967       return MP_NO_ACTION;
14968 
14969     if (IS_EM_GATE(element))
14970     {
14971       if (!player->key[EM_GATE_NR(element)])
14972 	return MP_NO_ACTION;
14973     }
14974     else if (IS_EM_GATE_GRAY(element))
14975     {
14976       if (!player->key[EM_GATE_GRAY_NR(element)])
14977 	return MP_NO_ACTION;
14978     }
14979     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14980     {
14981       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14982 	return MP_NO_ACTION;
14983     }
14984     else if (IS_EMC_GATE(element))
14985     {
14986       if (!player->key[EMC_GATE_NR(element)])
14987 	return MP_NO_ACTION;
14988     }
14989     else if (IS_EMC_GATE_GRAY(element))
14990     {
14991       if (!player->key[EMC_GATE_GRAY_NR(element)])
14992 	return MP_NO_ACTION;
14993     }
14994     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14995     {
14996       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14997 	return MP_NO_ACTION;
14998     }
14999     else if (element == EL_DC_GATE_WHITE ||
15000 	     element == EL_DC_GATE_WHITE_GRAY ||
15001 	     element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15002     {
15003       if (player->num_white_keys == 0)
15004 	return MP_NO_ACTION;
15005 
15006       player->num_white_keys--;
15007     }
15008     else if (IS_SP_PORT(element))
15009     {
15010       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15011 	  element == EL_SP_GRAVITY_PORT_RIGHT ||
15012 	  element == EL_SP_GRAVITY_PORT_UP ||
15013 	  element == EL_SP_GRAVITY_PORT_DOWN)
15014 #if USE_PLAYER_GRAVITY
15015 	player->gravity = !player->gravity;
15016 #else
15017 	game.gravity = !game.gravity;
15018 #endif
15019       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15020 	       element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15021 	       element == EL_SP_GRAVITY_ON_PORT_UP ||
15022 	       element == EL_SP_GRAVITY_ON_PORT_DOWN)
15023 #if USE_PLAYER_GRAVITY
15024 	player->gravity = TRUE;
15025 #else
15026 	game.gravity = TRUE;
15027 #endif
15028       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15029 	       element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15030 	       element == EL_SP_GRAVITY_OFF_PORT_UP ||
15031 	       element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15032 #if USE_PLAYER_GRAVITY
15033 	player->gravity = FALSE;
15034 #else
15035 	game.gravity = FALSE;
15036 #endif
15037     }
15038 
15039     /* automatically move to the next field with double speed */
15040     player->programmed_action = move_direction;
15041 
15042     if (player->move_delay_reset_counter == 0)
15043     {
15044       player->move_delay_reset_counter = 2;	/* two double speed steps */
15045 
15046       DOUBLE_PLAYER_SPEED(player);
15047     }
15048 
15049     PlayLevelSoundAction(x, y, ACTION_PASSING);
15050   }
15051   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15052   {
15053     RemoveField(x, y);
15054 
15055     if (mode != DF_SNAP)
15056     {
15057       GfxElement[x][y] = GFX_ELEMENT(element);
15058       player->is_digging = TRUE;
15059     }
15060 
15061     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15062 
15063     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15064 					player->index_bit, dig_side);
15065 
15066     if (mode == DF_SNAP)
15067     {
15068 #if USE_NEW_SNAP_DELAY
15069       if (level.block_snap_field)
15070 	setFieldForSnapping(x, y, element, move_direction);
15071       else
15072 	TestIfElementTouchesCustomElement(x, y);	/* for empty space */
15073 #else
15074       TestIfElementTouchesCustomElement(x, y);		/* for empty space */
15075 #endif
15076 
15077       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15078 					  player->index_bit, dig_side);
15079     }
15080   }
15081   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15082   {
15083     RemoveField(x, y);
15084 
15085     if (is_player && mode != DF_SNAP)
15086     {
15087       GfxElement[x][y] = element;
15088       player->is_collecting = TRUE;
15089     }
15090 
15091     if (element == EL_SPEED_PILL)
15092     {
15093       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15094     }
15095     else if (element == EL_EXTRA_TIME && level.time > 0)
15096     {
15097       TimeLeft += level.extra_time;
15098 
15099 #if 1
15100       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15101 
15102       DisplayGameControlValues();
15103 #else
15104       DrawGameValue_Time(TimeLeft);
15105 #endif
15106     }
15107     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15108     {
15109       player->shield_normal_time_left += level.shield_normal_time;
15110       if (element == EL_SHIELD_DEADLY)
15111 	player->shield_deadly_time_left += level.shield_deadly_time;
15112     }
15113     else if (element == EL_DYNAMITE ||
15114 	     element == EL_EM_DYNAMITE ||
15115 	     element == EL_SP_DISK_RED)
15116     {
15117       if (player->inventory_size < MAX_INVENTORY_SIZE)
15118 	player->inventory_element[player->inventory_size++] = element;
15119 
15120       DrawGameDoorValues();
15121     }
15122     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15123     {
15124       player->dynabomb_count++;
15125       player->dynabombs_left++;
15126     }
15127     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15128     {
15129       player->dynabomb_size++;
15130     }
15131     else if (element == EL_DYNABOMB_INCREASE_POWER)
15132     {
15133       player->dynabomb_xl = TRUE;
15134     }
15135     else if (IS_KEY(element))
15136     {
15137       player->key[KEY_NR(element)] = TRUE;
15138 
15139       DrawGameDoorValues();
15140     }
15141     else if (element == EL_DC_KEY_WHITE)
15142     {
15143       player->num_white_keys++;
15144 
15145       /* display white keys? */
15146       /* DrawGameDoorValues(); */
15147     }
15148     else if (IS_ENVELOPE(element))
15149     {
15150       player->show_envelope = element;
15151     }
15152     else if (element == EL_EMC_LENSES)
15153     {
15154       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15155 
15156       RedrawAllInvisibleElementsForLenses();
15157     }
15158     else if (element == EL_EMC_MAGNIFIER)
15159     {
15160       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15161 
15162       RedrawAllInvisibleElementsForMagnifier();
15163     }
15164     else if (IS_DROPPABLE(element) ||
15165 	     IS_THROWABLE(element))	/* can be collected and dropped */
15166     {
15167       int i;
15168 
15169       if (collect_count == 0)
15170 	player->inventory_infinite_element = element;
15171       else
15172 	for (i = 0; i < collect_count; i++)
15173 	  if (player->inventory_size < MAX_INVENTORY_SIZE)
15174 	    player->inventory_element[player->inventory_size++] = element;
15175 
15176       DrawGameDoorValues();
15177     }
15178     else if (collect_count > 0)
15179     {
15180       local_player->gems_still_needed -= collect_count;
15181       if (local_player->gems_still_needed < 0)
15182 	local_player->gems_still_needed = 0;
15183 
15184 #if 1
15185       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15186 
15187       DisplayGameControlValues();
15188 #else
15189       DrawGameValue_Emeralds(local_player->gems_still_needed);
15190 #endif
15191     }
15192 
15193     RaiseScoreElement(element);
15194     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15195 
15196     if (is_player)
15197       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15198 					  player->index_bit, dig_side);
15199 
15200     if (mode == DF_SNAP)
15201     {
15202 #if USE_NEW_SNAP_DELAY
15203       if (level.block_snap_field)
15204 	setFieldForSnapping(x, y, element, move_direction);
15205       else
15206 	TestIfElementTouchesCustomElement(x, y);	/* for empty space */
15207 #else
15208       TestIfElementTouchesCustomElement(x, y);		/* for empty space */
15209 #endif
15210 
15211       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15212 					  player->index_bit, dig_side);
15213     }
15214   }
15215   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15216   {
15217     if (mode == DF_SNAP && element != EL_BD_ROCK)
15218       return MP_NO_ACTION;
15219 
15220     if (CAN_FALL(element) && dy)
15221       return MP_NO_ACTION;
15222 
15223     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15224 	!(element == EL_SPRING && level.use_spring_bug))
15225       return MP_NO_ACTION;
15226 
15227     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15228 	((move_direction & MV_VERTICAL &&
15229 	  ((element_info[element].move_pattern & MV_LEFT &&
15230 	    IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15231 	   (element_info[element].move_pattern & MV_RIGHT &&
15232 	    IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15233 	 (move_direction & MV_HORIZONTAL &&
15234 	  ((element_info[element].move_pattern & MV_UP &&
15235 	    IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15236 	   (element_info[element].move_pattern & MV_DOWN &&
15237 	    IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15238       return MP_NO_ACTION;
15239 
15240     /* do not push elements already moving away faster than player */
15241     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15242 	ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15243       return MP_NO_ACTION;
15244 
15245     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15246     {
15247       if (player->push_delay_value == -1 || !player_was_pushing)
15248 	player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15249     }
15250     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15251     {
15252       if (player->push_delay_value == -1)
15253 	player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15254     }
15255     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15256     {
15257       if (!player->is_pushing)
15258 	player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15259     }
15260 
15261     player->is_pushing = TRUE;
15262     player->is_active = TRUE;
15263 
15264     if (!(IN_LEV_FIELD(nextx, nexty) &&
15265 	  (IS_FREE(nextx, nexty) ||
15266 	   (IS_SB_ELEMENT(element) &&
15267 	    Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15268 	   (IS_CUSTOM_ELEMENT(element) &&
15269 	    CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15270       return MP_NO_ACTION;
15271 
15272     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15273       return MP_NO_ACTION;
15274 
15275     if (player->push_delay == -1)	/* new pushing; restart delay */
15276       player->push_delay = 0;
15277 
15278     if (player->push_delay < player->push_delay_value &&
15279 	!(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15280 	element != EL_SPRING && element != EL_BALLOON)
15281     {
15282       /* make sure that there is no move delay before next try to push */
15283       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15284 	player->move_delay = 0;
15285 
15286       return MP_NO_ACTION;
15287     }
15288 
15289     if (IS_CUSTOM_ELEMENT(element) &&
15290 	CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15291     {
15292       if (!DigFieldByCE(nextx, nexty, element))
15293 	return MP_NO_ACTION;
15294     }
15295 
15296     if (IS_SB_ELEMENT(element))
15297     {
15298       if (element == EL_SOKOBAN_FIELD_FULL)
15299       {
15300 	Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15301 	local_player->sokobanfields_still_needed++;
15302       }
15303 
15304       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15305       {
15306 	Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15307 	local_player->sokobanfields_still_needed--;
15308       }
15309 
15310       Feld[x][y] = EL_SOKOBAN_OBJECT;
15311 
15312       if (Back[x][y] == Back[nextx][nexty])
15313 	PlayLevelSoundAction(x, y, ACTION_PUSHING);
15314       else if (Back[x][y] != 0)
15315 	PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15316 				    ACTION_EMPTYING);
15317       else
15318 	PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15319 				    ACTION_FILLING);
15320 
15321 #if 1
15322       if (local_player->sokobanfields_still_needed == 0 &&
15323 	  (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15324 #else
15325       if (local_player->sokobanfields_still_needed == 0 &&
15326 	  game.emulation == EMU_SOKOBAN)
15327 #endif
15328       {
15329 	PlayerWins(player);
15330 
15331 	PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15332       }
15333     }
15334     else
15335       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15336 
15337     InitMovingField(x, y, move_direction);
15338     GfxAction[x][y] = ACTION_PUSHING;
15339 
15340     if (mode == DF_SNAP)
15341       ContinueMoving(x, y);
15342     else
15343       MovPos[x][y] = (dx != 0 ? dx : dy);
15344 
15345     Pushed[x][y] = TRUE;
15346     Pushed[nextx][nexty] = TRUE;
15347 
15348     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15349       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15350     else
15351       player->push_delay_value = -1;	/* get new value later */
15352 
15353     /* check for element change _after_ element has been pushed */
15354     if (game.use_change_when_pushing_bug)
15355     {
15356       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15357 				 player->index_bit, dig_side);
15358       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15359 					  player->index_bit, dig_side);
15360     }
15361   }
15362   else if (IS_SWITCHABLE(element))
15363   {
15364     if (PLAYER_SWITCHING(player, x, y))
15365     {
15366       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15367 					  player->index_bit, dig_side);
15368 
15369       return MP_ACTION;
15370     }
15371 
15372     player->is_switching = TRUE;
15373     player->switch_x = x;
15374     player->switch_y = y;
15375 
15376     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15377 
15378     if (element == EL_ROBOT_WHEEL)
15379     {
15380       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15381       ZX = x;
15382       ZY = y;
15383 
15384       game.robot_wheel_active = TRUE;
15385 
15386       TEST_DrawLevelField(x, y);
15387     }
15388     else if (element == EL_SP_TERMINAL)
15389     {
15390       int xx, yy;
15391 
15392       SCAN_PLAYFIELD(xx, yy)
15393       {
15394 	if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15395 	  Bang(xx, yy);
15396 	else if (Feld[xx][yy] == EL_SP_TERMINAL)
15397 	  Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15398       }
15399     }
15400     else if (IS_BELT_SWITCH(element))
15401     {
15402       ToggleBeltSwitch(x, y);
15403     }
15404     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15405 	     element == EL_SWITCHGATE_SWITCH_DOWN ||
15406 	     element == EL_DC_SWITCHGATE_SWITCH_UP ||
15407 	     element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15408     {
15409       ToggleSwitchgateSwitch(x, y);
15410     }
15411     else if (element == EL_LIGHT_SWITCH ||
15412 	     element == EL_LIGHT_SWITCH_ACTIVE)
15413     {
15414       ToggleLightSwitch(x, y);
15415     }
15416     else if (element == EL_TIMEGATE_SWITCH ||
15417 	     element == EL_DC_TIMEGATE_SWITCH)
15418     {
15419       ActivateTimegateSwitch(x, y);
15420     }
15421     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15422 	     element == EL_BALLOON_SWITCH_RIGHT ||
15423 	     element == EL_BALLOON_SWITCH_UP    ||
15424 	     element == EL_BALLOON_SWITCH_DOWN  ||
15425 	     element == EL_BALLOON_SWITCH_NONE  ||
15426 	     element == EL_BALLOON_SWITCH_ANY)
15427     {
15428       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15429 			     element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15430 			     element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15431 			     element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15432 			     element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15433 			     move_direction);
15434     }
15435     else if (element == EL_LAMP)
15436     {
15437       Feld[x][y] = EL_LAMP_ACTIVE;
15438       local_player->lights_still_needed--;
15439 
15440       ResetGfxAnimation(x, y);
15441       TEST_DrawLevelField(x, y);
15442     }
15443     else if (element == EL_TIME_ORB_FULL)
15444     {
15445       Feld[x][y] = EL_TIME_ORB_EMPTY;
15446 
15447       if (level.time > 0 || level.use_time_orb_bug)
15448       {
15449 	TimeLeft += level.time_orb_time;
15450 	game.no_time_limit = FALSE;
15451 
15452 #if 1
15453 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15454 
15455 	DisplayGameControlValues();
15456 #else
15457 	DrawGameValue_Time(TimeLeft);
15458 #endif
15459       }
15460 
15461       ResetGfxAnimation(x, y);
15462       TEST_DrawLevelField(x, y);
15463     }
15464     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15465 	     element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15466     {
15467       int xx, yy;
15468 
15469       game.ball_state = !game.ball_state;
15470 
15471       SCAN_PLAYFIELD(xx, yy)
15472       {
15473 	int e = Feld[xx][yy];
15474 
15475 	if (game.ball_state)
15476 	{
15477 	  if (e == EL_EMC_MAGIC_BALL)
15478 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15479 	  else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15480 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15481 	}
15482 	else
15483 	{
15484 	  if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15485 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15486 	  else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15487 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15488 	}
15489       }
15490     }
15491 
15492     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15493 					player->index_bit, dig_side);
15494 
15495     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15496 					player->index_bit, dig_side);
15497 
15498     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15499 					player->index_bit, dig_side);
15500 
15501     return MP_ACTION;
15502   }
15503   else
15504   {
15505     if (!PLAYER_SWITCHING(player, x, y))
15506     {
15507       player->is_switching = TRUE;
15508       player->switch_x = x;
15509       player->switch_y = y;
15510 
15511       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15512 				 player->index_bit, dig_side);
15513       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15514 					  player->index_bit, dig_side);
15515 
15516       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15517 				 player->index_bit, dig_side);
15518       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15519 					  player->index_bit, dig_side);
15520     }
15521 
15522     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15523 			       player->index_bit, dig_side);
15524     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15525 					player->index_bit, dig_side);
15526 
15527     return MP_NO_ACTION;
15528   }
15529 
15530   player->push_delay = -1;
15531 
15532   if (is_player)		/* function can also be called by EL_PENGUIN */
15533   {
15534     if (Feld[x][y] != element)		/* really digged/collected something */
15535     {
15536       player->is_collecting = !player->is_digging;
15537       player->is_active = TRUE;
15538     }
15539   }
15540 
15541   return MP_MOVING;
15542 }
15543 
DigFieldByCE(int x,int y,int digging_element)15544 static boolean DigFieldByCE(int x, int y, int digging_element)
15545 {
15546   int element = Feld[x][y];
15547 
15548   if (!IS_FREE(x, y))
15549   {
15550     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15551 		  IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15552 		  ACTION_BREAKING);
15553 
15554     /* no element can dig solid indestructible elements */
15555     if (IS_INDESTRUCTIBLE(element) &&
15556 	!IS_DIGGABLE(element) &&
15557 	!IS_COLLECTIBLE(element))
15558       return FALSE;
15559 
15560     if (AmoebaNr[x][y] &&
15561 	(element == EL_AMOEBA_FULL ||
15562 	 element == EL_BD_AMOEBA ||
15563 	 element == EL_AMOEBA_GROWING))
15564     {
15565       AmoebaCnt[AmoebaNr[x][y]]--;
15566       AmoebaCnt2[AmoebaNr[x][y]]--;
15567     }
15568 
15569     if (IS_MOVING(x, y))
15570       RemoveMovingField(x, y);
15571     else
15572     {
15573       RemoveField(x, y);
15574       TEST_DrawLevelField(x, y);
15575     }
15576 
15577     /* if digged element was about to explode, prevent the explosion */
15578     ExplodeField[x][y] = EX_TYPE_NONE;
15579 
15580     PlayLevelSoundAction(x, y, action);
15581   }
15582 
15583   Store[x][y] = EL_EMPTY;
15584 
15585 #if 1
15586   /* this makes it possible to leave the removed element again */
15587   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15588     Store[x][y] = element;
15589 #else
15590   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15591   {
15592     int move_leave_element = element_info[digging_element].move_leave_element;
15593 
15594     /* this makes it possible to leave the removed element again */
15595     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15596 		   element : move_leave_element);
15597   }
15598 #endif
15599 
15600   return TRUE;
15601 }
15602 
SnapField(struct PlayerInfo * player,int dx,int dy)15603 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15604 {
15605   int jx = player->jx, jy = player->jy;
15606   int x = jx + dx, y = jy + dy;
15607   int snap_direction = (dx == -1 ? MV_LEFT  :
15608 			dx == +1 ? MV_RIGHT :
15609 			dy == -1 ? MV_UP    :
15610 			dy == +1 ? MV_DOWN  : MV_NONE);
15611   boolean can_continue_snapping = (level.continuous_snapping &&
15612 				   WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15613 
15614   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15615     return FALSE;
15616 
15617   if (!player->active || !IN_LEV_FIELD(x, y))
15618     return FALSE;
15619 
15620   if (dx && dy)
15621     return FALSE;
15622 
15623   if (!dx && !dy)
15624   {
15625     if (player->MovPos == 0)
15626       player->is_pushing = FALSE;
15627 
15628     player->is_snapping = FALSE;
15629 
15630     if (player->MovPos == 0)
15631     {
15632       player->is_moving = FALSE;
15633       player->is_digging = FALSE;
15634       player->is_collecting = FALSE;
15635     }
15636 
15637     return FALSE;
15638   }
15639 
15640 #if USE_NEW_CONTINUOUS_SNAPPING
15641   /* prevent snapping with already pressed snap key when not allowed */
15642   if (player->is_snapping && !can_continue_snapping)
15643     return FALSE;
15644 #else
15645   if (player->is_snapping)
15646     return FALSE;
15647 #endif
15648 
15649   player->MovDir = snap_direction;
15650 
15651   if (player->MovPos == 0)
15652   {
15653     player->is_moving = FALSE;
15654     player->is_digging = FALSE;
15655     player->is_collecting = FALSE;
15656   }
15657 
15658   player->is_dropping = FALSE;
15659   player->is_dropping_pressed = FALSE;
15660   player->drop_pressed_delay = 0;
15661 
15662   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15663     return FALSE;
15664 
15665   player->is_snapping = TRUE;
15666   player->is_active = TRUE;
15667 
15668   if (player->MovPos == 0)
15669   {
15670     player->is_moving = FALSE;
15671     player->is_digging = FALSE;
15672     player->is_collecting = FALSE;
15673   }
15674 
15675   if (player->MovPos != 0)	/* prevent graphic bugs in versions < 2.2.0 */
15676     TEST_DrawLevelField(player->last_jx, player->last_jy);
15677 
15678   TEST_DrawLevelField(x, y);
15679 
15680   return TRUE;
15681 }
15682 
DropElement(struct PlayerInfo * player)15683 static boolean DropElement(struct PlayerInfo *player)
15684 {
15685   int old_element, new_element;
15686   int dropx = player->jx, dropy = player->jy;
15687   int drop_direction = player->MovDir;
15688   int drop_side = drop_direction;
15689 #if 1
15690   int drop_element = get_next_dropped_element(player);
15691 #else
15692   int drop_element = (player->inventory_size > 0 ?
15693 		      player->inventory_element[player->inventory_size - 1] :
15694 		      player->inventory_infinite_element != EL_UNDEFINED ?
15695 		      player->inventory_infinite_element :
15696 		      player->dynabombs_left > 0 ?
15697 		      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15698 		      EL_UNDEFINED);
15699 #endif
15700 
15701   player->is_dropping_pressed = TRUE;
15702 
15703   /* do not drop an element on top of another element; when holding drop key
15704      pressed without moving, dropped element must move away before the next
15705      element can be dropped (this is especially important if the next element
15706      is dynamite, which can be placed on background for historical reasons) */
15707   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15708     return MP_ACTION;
15709 
15710   if (IS_THROWABLE(drop_element))
15711   {
15712     dropx += GET_DX_FROM_DIR(drop_direction);
15713     dropy += GET_DY_FROM_DIR(drop_direction);
15714 
15715     if (!IN_LEV_FIELD(dropx, dropy))
15716       return FALSE;
15717   }
15718 
15719   old_element = Feld[dropx][dropy];	/* old element at dropping position */
15720   new_element = drop_element;		/* default: no change when dropping */
15721 
15722   /* check if player is active, not moving and ready to drop */
15723   if (!player->active || player->MovPos || player->drop_delay > 0)
15724     return FALSE;
15725 
15726   /* check if player has anything that can be dropped */
15727   if (new_element == EL_UNDEFINED)
15728     return FALSE;
15729 
15730   /* check if drop key was pressed long enough for EM style dynamite */
15731   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15732     return FALSE;
15733 
15734   /* check if anything can be dropped at the current position */
15735   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15736     return FALSE;
15737 
15738   /* collected custom elements can only be dropped on empty fields */
15739   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15740     return FALSE;
15741 
15742   if (old_element != EL_EMPTY)
15743     Back[dropx][dropy] = old_element;	/* store old element on this field */
15744 
15745   ResetGfxAnimation(dropx, dropy);
15746   ResetRandomAnimationValue(dropx, dropy);
15747 
15748   if (player->inventory_size > 0 ||
15749       player->inventory_infinite_element != EL_UNDEFINED)
15750   {
15751     if (player->inventory_size > 0)
15752     {
15753       player->inventory_size--;
15754 
15755       DrawGameDoorValues();
15756 
15757       if (new_element == EL_DYNAMITE)
15758 	new_element = EL_DYNAMITE_ACTIVE;
15759       else if (new_element == EL_EM_DYNAMITE)
15760 	new_element = EL_EM_DYNAMITE_ACTIVE;
15761       else if (new_element == EL_SP_DISK_RED)
15762 	new_element = EL_SP_DISK_RED_ACTIVE;
15763     }
15764 
15765     Feld[dropx][dropy] = new_element;
15766 
15767     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15768       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15769 			  el2img(Feld[dropx][dropy]), 0);
15770 
15771     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15772 
15773     /* needed if previous element just changed to "empty" in the last frame */
15774     ChangeCount[dropx][dropy] = 0;	/* allow at least one more change */
15775 
15776     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15777 			       player->index_bit, drop_side);
15778     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15779 					CE_PLAYER_DROPS_X,
15780 					player->index_bit, drop_side);
15781 
15782     TestIfElementTouchesCustomElement(dropx, dropy);
15783   }
15784   else		/* player is dropping a dyna bomb */
15785   {
15786     player->dynabombs_left--;
15787 
15788     Feld[dropx][dropy] = new_element;
15789 
15790     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15791       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15792 			  el2img(Feld[dropx][dropy]), 0);
15793 
15794     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15795   }
15796 
15797   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15798     InitField_WithBug1(dropx, dropy, FALSE);
15799 
15800   new_element = Feld[dropx][dropy];	/* element might have changed */
15801 
15802   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15803       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15804   {
15805 #if 0
15806     int move_direction;
15807     int nextx, nexty;
15808 #endif
15809 
15810     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15811       MovDir[dropx][dropy] = drop_direction;
15812 
15813 #if 0
15814     move_direction = MovDir[dropx][dropy];
15815     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15816     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15817 #endif
15818 
15819     ChangeCount[dropx][dropy] = 0;	/* allow at least one more change */
15820 
15821 #if USE_FIX_IMPACT_COLLISION
15822     /* do not cause impact style collision by dropping elements that can fall */
15823     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15824 #else
15825     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15826 #endif
15827   }
15828 
15829   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15830   player->is_dropping = TRUE;
15831 
15832   player->drop_pressed_delay = 0;
15833   player->is_dropping_pressed = FALSE;
15834 
15835   player->drop_x = dropx;
15836   player->drop_y = dropy;
15837 
15838   return TRUE;
15839 }
15840 
15841 /* ------------------------------------------------------------------------- */
15842 /* game sound playing functions                                              */
15843 /* ------------------------------------------------------------------------- */
15844 
15845 static int *loop_sound_frame = NULL;
15846 static int *loop_sound_volume = NULL;
15847 
InitPlayLevelSound()15848 void InitPlayLevelSound()
15849 {
15850   int num_sounds = getSoundListSize();
15851 
15852   checked_free(loop_sound_frame);
15853   checked_free(loop_sound_volume);
15854 
15855   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15856   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15857 }
15858 
PlayLevelSound(int x,int y,int nr)15859 static void PlayLevelSound(int x, int y, int nr)
15860 {
15861   int sx = SCREENX(x), sy = SCREENY(y);
15862   int volume, stereo_position;
15863   int max_distance = 8;
15864   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15865 
15866   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15867       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15868     return;
15869 
15870   if (!IN_LEV_FIELD(x, y) ||
15871       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15872       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15873     return;
15874 
15875   volume = SOUND_MAX_VOLUME;
15876 
15877   if (!IN_SCR_FIELD(sx, sy))
15878   {
15879     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15880     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15881 
15882     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15883   }
15884 
15885   stereo_position = (SOUND_MAX_LEFT +
15886 		     (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15887 		     (SCR_FIELDX + 2 * max_distance));
15888 
15889   if (IS_LOOP_SOUND(nr))
15890   {
15891     /* This assures that quieter loop sounds do not overwrite louder ones,
15892        while restarting sound volume comparison with each new game frame. */
15893 
15894     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15895       return;
15896 
15897     loop_sound_volume[nr] = volume;
15898     loop_sound_frame[nr] = FrameCounter;
15899   }
15900 
15901   PlaySoundExt(nr, volume, stereo_position, type);
15902 }
15903 
PlayLevelSoundNearest(int x,int y,int sound_action)15904 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15905 {
15906   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15907 		 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15908 		 y < LEVELY(BY1) ? LEVELY(BY1) :
15909 		 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15910 		 sound_action);
15911 }
15912 
PlayLevelSoundAction(int x,int y,int action)15913 static void PlayLevelSoundAction(int x, int y, int action)
15914 {
15915   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15916 }
15917 
PlayLevelSoundElementAction(int x,int y,int element,int action)15918 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15919 {
15920   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15921 
15922   if (sound_effect != SND_UNDEFINED)
15923     PlayLevelSound(x, y, sound_effect);
15924 }
15925 
PlayLevelSoundElementActionIfLoop(int x,int y,int element,int action)15926 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15927 					      int action)
15928 {
15929   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15930 
15931   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15932     PlayLevelSound(x, y, sound_effect);
15933 }
15934 
PlayLevelSoundActionIfLoop(int x,int y,int action)15935 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15936 {
15937   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15938 
15939   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15940     PlayLevelSound(x, y, sound_effect);
15941 }
15942 
StopLevelSoundActionIfLoop(int x,int y,int action)15943 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15944 {
15945   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15946 
15947   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15948     StopSound(sound_effect);
15949 }
15950 
PlayLevelMusic()15951 static void PlayLevelMusic()
15952 {
15953   if (levelset.music[level_nr] != MUS_UNDEFINED)
15954     PlayMusic(levelset.music[level_nr]);	/* from config file */
15955   else
15956     PlayMusic(MAP_NOCONF_MUSIC(level_nr));	/* from music dir */
15957 }
15958 
PlayLevelSound_EM(int xx,int yy,int element_em,int sample)15959 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15960 {
15961   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15962   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15963   int x = xx - 1 - offset;
15964   int y = yy - 1 - offset;
15965 
15966   switch (sample)
15967   {
15968     case SAMPLE_blank:
15969       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15970       break;
15971 
15972     case SAMPLE_roll:
15973       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15974       break;
15975 
15976     case SAMPLE_stone:
15977       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15978       break;
15979 
15980     case SAMPLE_nut:
15981       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15982       break;
15983 
15984     case SAMPLE_crack:
15985       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15986       break;
15987 
15988     case SAMPLE_bug:
15989       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15990       break;
15991 
15992     case SAMPLE_tank:
15993       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15994       break;
15995 
15996     case SAMPLE_android_clone:
15997       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15998       break;
15999 
16000     case SAMPLE_android_move:
16001       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16002       break;
16003 
16004     case SAMPLE_spring:
16005       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16006       break;
16007 
16008     case SAMPLE_slurp:
16009       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16010       break;
16011 
16012     case SAMPLE_eater:
16013       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16014       break;
16015 
16016     case SAMPLE_eater_eat:
16017       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16018       break;
16019 
16020     case SAMPLE_alien:
16021       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16022       break;
16023 
16024     case SAMPLE_collect:
16025       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16026       break;
16027 
16028     case SAMPLE_diamond:
16029       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16030       break;
16031 
16032     case SAMPLE_squash:
16033       /* !!! CHECK THIS !!! */
16034 #if 1
16035       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16036 #else
16037       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16038 #endif
16039       break;
16040 
16041     case SAMPLE_wonderfall:
16042       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16043       break;
16044 
16045     case SAMPLE_drip:
16046       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16047       break;
16048 
16049     case SAMPLE_push:
16050       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16051       break;
16052 
16053     case SAMPLE_dirt:
16054       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16055       break;
16056 
16057     case SAMPLE_acid:
16058       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16059       break;
16060 
16061     case SAMPLE_ball:
16062       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16063       break;
16064 
16065     case SAMPLE_grow:
16066       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16067       break;
16068 
16069     case SAMPLE_wonder:
16070       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16071       break;
16072 
16073     case SAMPLE_door:
16074       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16075       break;
16076 
16077     case SAMPLE_exit_open:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16079       break;
16080 
16081     case SAMPLE_exit_leave:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16083       break;
16084 
16085     case SAMPLE_dynamite:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16087       break;
16088 
16089     case SAMPLE_tick:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16091       break;
16092 
16093     case SAMPLE_press:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16095       break;
16096 
16097     case SAMPLE_wheel:
16098       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16099       break;
16100 
16101     case SAMPLE_boom:
16102       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16103       break;
16104 
16105     case SAMPLE_die:
16106       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16107       break;
16108 
16109     case SAMPLE_time:
16110       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16111       break;
16112 
16113     default:
16114       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16115       break;
16116   }
16117 }
16118 
PlayLevelSound_SP(int xx,int yy,int element_sp,int action_sp)16119 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16120 {
16121   int element = map_element_SP_to_RND(element_sp);
16122   int action = map_action_SP_to_RND(action_sp);
16123   int offset = (setup.sp_show_border_elements ? 0 : 1);
16124   int x = xx - offset;
16125   int y = yy - offset;
16126 
16127 #if 0
16128   printf("::: %d -> %d\n", element_sp, action_sp);
16129 #endif
16130 
16131   PlayLevelSoundElementAction(x, y, element, action);
16132 }
16133 
RaiseScore(int value)16134 void RaiseScore(int value)
16135 {
16136   local_player->score += value;
16137 
16138 #if 1
16139   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16140 
16141   DisplayGameControlValues();
16142 #else
16143   DrawGameValue_Score(local_player->score);
16144 #endif
16145 }
16146 
RaiseScoreElement(int element)16147 void RaiseScoreElement(int element)
16148 {
16149   switch (element)
16150   {
16151     case EL_EMERALD:
16152     case EL_BD_DIAMOND:
16153     case EL_EMERALD_YELLOW:
16154     case EL_EMERALD_RED:
16155     case EL_EMERALD_PURPLE:
16156     case EL_SP_INFOTRON:
16157       RaiseScore(level.score[SC_EMERALD]);
16158       break;
16159     case EL_DIAMOND:
16160       RaiseScore(level.score[SC_DIAMOND]);
16161       break;
16162     case EL_CRYSTAL:
16163       RaiseScore(level.score[SC_CRYSTAL]);
16164       break;
16165     case EL_PEARL:
16166       RaiseScore(level.score[SC_PEARL]);
16167       break;
16168     case EL_BUG:
16169     case EL_BD_BUTTERFLY:
16170     case EL_SP_ELECTRON:
16171       RaiseScore(level.score[SC_BUG]);
16172       break;
16173     case EL_SPACESHIP:
16174     case EL_BD_FIREFLY:
16175     case EL_SP_SNIKSNAK:
16176       RaiseScore(level.score[SC_SPACESHIP]);
16177       break;
16178     case EL_YAMYAM:
16179     case EL_DARK_YAMYAM:
16180       RaiseScore(level.score[SC_YAMYAM]);
16181       break;
16182     case EL_ROBOT:
16183       RaiseScore(level.score[SC_ROBOT]);
16184       break;
16185     case EL_PACMAN:
16186       RaiseScore(level.score[SC_PACMAN]);
16187       break;
16188     case EL_NUT:
16189       RaiseScore(level.score[SC_NUT]);
16190       break;
16191     case EL_DYNAMITE:
16192     case EL_EM_DYNAMITE:
16193     case EL_SP_DISK_RED:
16194     case EL_DYNABOMB_INCREASE_NUMBER:
16195     case EL_DYNABOMB_INCREASE_SIZE:
16196     case EL_DYNABOMB_INCREASE_POWER:
16197       RaiseScore(level.score[SC_DYNAMITE]);
16198       break;
16199     case EL_SHIELD_NORMAL:
16200     case EL_SHIELD_DEADLY:
16201       RaiseScore(level.score[SC_SHIELD]);
16202       break;
16203     case EL_EXTRA_TIME:
16204       RaiseScore(level.extra_time_score);
16205       break;
16206     case EL_KEY_1:
16207     case EL_KEY_2:
16208     case EL_KEY_3:
16209     case EL_KEY_4:
16210     case EL_EM_KEY_1:
16211     case EL_EM_KEY_2:
16212     case EL_EM_KEY_3:
16213     case EL_EM_KEY_4:
16214     case EL_EMC_KEY_5:
16215     case EL_EMC_KEY_6:
16216     case EL_EMC_KEY_7:
16217     case EL_EMC_KEY_8:
16218     case EL_DC_KEY_WHITE:
16219       RaiseScore(level.score[SC_KEY]);
16220       break;
16221     default:
16222       RaiseScore(element_info[element].collect_score);
16223       break;
16224   }
16225 }
16226 
RequestQuitGameExt(boolean skip_request,boolean quick_quit,char * message)16227 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16228 {
16229   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16230   {
16231 #if defined(NETWORK_AVALIABLE)
16232     if (options.network)
16233       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16234     else
16235 #endif
16236     {
16237       if (quick_quit)
16238       {
16239 #if 1
16240 
16241 #if 1
16242 	FadeSkipNextFadeIn();
16243 #else
16244 	fading = fading_none;
16245 #endif
16246 
16247 #else
16248 	OpenDoor(DOOR_CLOSE_1);
16249 #endif
16250 
16251 	game_status = GAME_MODE_MAIN;
16252 
16253 #if 1
16254 	DrawAndFadeInMainMenu(REDRAW_FIELD);
16255 #else
16256 	DrawMainMenu();
16257 #endif
16258       }
16259       else
16260       {
16261 #if 0
16262 	FadeOut(REDRAW_FIELD);
16263 #endif
16264 
16265 	game_status = GAME_MODE_MAIN;
16266 
16267 	DrawAndFadeInMainMenu(REDRAW_FIELD);
16268       }
16269     }
16270   }
16271   else		/* continue playing the game */
16272   {
16273     if (tape.playing && tape.deactivate_display)
16274       TapeDeactivateDisplayOff(TRUE);
16275 
16276     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16277 
16278     if (tape.playing && tape.deactivate_display)
16279       TapeDeactivateDisplayOn();
16280   }
16281 }
16282 
RequestQuitGame(boolean ask_if_really_quit)16283 void RequestQuitGame(boolean ask_if_really_quit)
16284 {
16285   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16286   boolean skip_request = AllPlayersGone || quick_quit;
16287 
16288   RequestQuitGameExt(skip_request, quick_quit,
16289 		     "Do you really want to quit the game ?");
16290 }
16291 
16292 
16293 /* ------------------------------------------------------------------------- */
16294 /* random generator functions                                                */
16295 /* ------------------------------------------------------------------------- */
16296 
InitEngineRandom_RND(int seed)16297 unsigned int InitEngineRandom_RND(int seed)
16298 {
16299   game.num_random_calls = 0;
16300 
16301 #if 0
16302   unsigned int rnd_seed = InitEngineRandom(seed);
16303 
16304   printf("::: START RND: %d\n", rnd_seed);
16305 
16306   return rnd_seed;
16307 #else
16308 
16309   return InitEngineRandom(seed);
16310 
16311 #endif
16312 
16313 }
16314 
RND(int max)16315 unsigned int RND(int max)
16316 {
16317   if (max > 0)
16318   {
16319     game.num_random_calls++;
16320 
16321     return GetEngineRandom(max);
16322   }
16323 
16324   return 0;
16325 }
16326 
16327 
16328 /* ------------------------------------------------------------------------- */
16329 /* game engine snapshot handling functions                                   */
16330 /* ------------------------------------------------------------------------- */
16331 
16332 struct EngineSnapshotInfo
16333 {
16334   /* runtime values for custom element collect score */
16335   int collect_score[NUM_CUSTOM_ELEMENTS];
16336 
16337   /* runtime values for group element choice position */
16338   int choice_pos[NUM_GROUP_ELEMENTS];
16339 
16340   /* runtime values for belt position animations */
16341   int belt_graphic[4][NUM_BELT_PARTS];
16342   int belt_anim_mode[4][NUM_BELT_PARTS];
16343 };
16344 
16345 static struct EngineSnapshotInfo engine_snapshot_rnd;
16346 static char *snapshot_level_identifier = NULL;
16347 static int snapshot_level_nr = -1;
16348 
SaveEngineSnapshotValues_RND()16349 static void SaveEngineSnapshotValues_RND()
16350 {
16351   static int belt_base_active_element[4] =
16352   {
16353     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16354     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16355     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16356     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16357   };
16358   int i, j;
16359 
16360   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16361   {
16362     int element = EL_CUSTOM_START + i;
16363 
16364     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16365   }
16366 
16367   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16368   {
16369     int element = EL_GROUP_START + i;
16370 
16371     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16372   }
16373 
16374   for (i = 0; i < 4; i++)
16375   {
16376     for (j = 0; j < NUM_BELT_PARTS; j++)
16377     {
16378       int element = belt_base_active_element[i] + j;
16379       int graphic = el2img(element);
16380       int anim_mode = graphic_info[graphic].anim_mode;
16381 
16382       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16383       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16384     }
16385   }
16386 }
16387 
LoadEngineSnapshotValues_RND()16388 static void LoadEngineSnapshotValues_RND()
16389 {
16390   unsigned int num_random_calls = game.num_random_calls;
16391   int i, j;
16392 
16393   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16394   {
16395     int element = EL_CUSTOM_START + i;
16396 
16397     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16398   }
16399 
16400   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16401   {
16402     int element = EL_GROUP_START + i;
16403 
16404     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16405   }
16406 
16407   for (i = 0; i < 4; i++)
16408   {
16409     for (j = 0; j < NUM_BELT_PARTS; j++)
16410     {
16411       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16412       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16413 
16414       graphic_info[graphic].anim_mode = anim_mode;
16415     }
16416   }
16417 
16418   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16419   {
16420     InitRND(tape.random_seed);
16421     for (i = 0; i < num_random_calls; i++)
16422       RND(1);
16423   }
16424 
16425   if (game.num_random_calls != num_random_calls)
16426   {
16427     Error(ERR_INFO, "number of random calls out of sync");
16428     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16429     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16430     Error(ERR_EXIT, "this should not happen -- please debug");
16431   }
16432 }
16433 
SaveEngineSnapshot()16434 void SaveEngineSnapshot()
16435 {
16436   /* do not save snapshots from editor */
16437   if (level_editor_test_game)
16438     return;
16439 
16440   /* free previous snapshot buffers, if needed */
16441   FreeEngineSnapshotBuffers();
16442 
16443   /* copy some special values to a structure better suited for the snapshot */
16444 
16445   SaveEngineSnapshotValues_RND();
16446   SaveEngineSnapshotValues_EM();
16447   SaveEngineSnapshotValues_SP();
16448 
16449   /* save values stored in special snapshot structure */
16450 
16451   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16452   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16453   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16454 
16455   /* save further RND engine values */
16456 
16457   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16458   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16459   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16460 
16461   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16462   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16463   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16464   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16465 
16466   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16467   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16468   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16469   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16470   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16471 
16472   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16473   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16474   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16475 
16476   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16477 
16478   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16479 
16480   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16481   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16482 
16483   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16484   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16485   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16486   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16487   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16488   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16489   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16490   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16491   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16492   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16493   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16494   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16495   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16496   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16497   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16498   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16499   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16500   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16501 
16502   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16503   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16504 
16505   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16506   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16507   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16508 
16509   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16510   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16511 
16512   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16513   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16514   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16515   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16516   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16517 
16518   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16519   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16520 
16521   /* save level identification information */
16522 
16523   setString(&snapshot_level_identifier, leveldir_current->identifier);
16524   snapshot_level_nr = level_nr;
16525 
16526 #if 0
16527   ListNode *node = engine_snapshot_list_rnd;
16528   int num_bytes = 0;
16529 
16530   while (node != NULL)
16531   {
16532     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16533 
16534     node = node->next;
16535   }
16536 
16537   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16538 #endif
16539 }
16540 
LoadEngineSnapshot()16541 void LoadEngineSnapshot()
16542 {
16543   /* restore generically stored snapshot buffers */
16544 
16545   LoadEngineSnapshotBuffers();
16546 
16547   /* restore special values from snapshot structure */
16548 
16549   LoadEngineSnapshotValues_RND();
16550   LoadEngineSnapshotValues_EM();
16551   LoadEngineSnapshotValues_SP();
16552 }
16553 
CheckEngineSnapshot()16554 boolean CheckEngineSnapshot()
16555 {
16556   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16557 	  snapshot_level_nr == level_nr);
16558 }
16559 
16560 
16561 /* ---------- new game button stuff ---------------------------------------- */
16562 
16563 static struct
16564 {
16565   int graphic;
16566   struct Rect *pos;
16567   int gadget_id;
16568   char *infotext;
16569 } gamebutton_info[NUM_GAME_BUTTONS] =
16570 {
16571   {
16572     IMG_GAME_BUTTON_GFX_STOP,		&game.button.stop,
16573     GAME_CTRL_ID_STOP,			"stop game"
16574   },
16575   {
16576     IMG_GAME_BUTTON_GFX_PAUSE,		&game.button.pause,
16577     GAME_CTRL_ID_PAUSE,			"pause game"
16578   },
16579   {
16580     IMG_GAME_BUTTON_GFX_PLAY,		&game.button.play,
16581     GAME_CTRL_ID_PLAY,			"play game"
16582   },
16583   {
16584     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,	&game.button.sound_music,
16585     SOUND_CTRL_ID_MUSIC,		"background music on/off"
16586   },
16587   {
16588     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,	&game.button.sound_loops,
16589     SOUND_CTRL_ID_LOOPS,		"sound loops on/off"
16590   },
16591   {
16592     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,	&game.button.sound_simple,
16593     SOUND_CTRL_ID_SIMPLE,		"normal sounds on/off"
16594   }
16595 };
16596 
CreateGameButtons()16597 void CreateGameButtons()
16598 {
16599   int i;
16600 
16601   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16602   {
16603     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
16604     struct Rect *pos = gamebutton_info[i].pos;
16605     struct GadgetInfo *gi;
16606     int button_type;
16607     boolean checked;
16608     unsigned int event_mask;
16609     int gd_x   = gfx->src_x;
16610     int gd_y   = gfx->src_y;
16611     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16612     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16613     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16614     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16615     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16616     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16617     int id = i;
16618 
16619     if (id == GAME_CTRL_ID_STOP ||
16620 	id == GAME_CTRL_ID_PAUSE ||
16621 	id == GAME_CTRL_ID_PLAY)
16622     {
16623       button_type = GD_TYPE_NORMAL_BUTTON;
16624       checked = FALSE;
16625       event_mask = GD_EVENT_RELEASED;
16626     }
16627     else
16628     {
16629       button_type = GD_TYPE_CHECK_BUTTON;
16630       checked =
16631 	((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16632 	 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16633 	 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16634       event_mask = GD_EVENT_PRESSED;
16635     }
16636 
16637     gi = CreateGadget(GDI_CUSTOM_ID, id,
16638 		      GDI_INFO_TEXT, gamebutton_info[i].infotext,
16639 		      GDI_X, DX + pos->x,
16640 		      GDI_Y, DY + pos->y,
16641 		      GDI_WIDTH, gfx->width,
16642 		      GDI_HEIGHT, gfx->height,
16643 		      GDI_TYPE, button_type,
16644 		      GDI_STATE, GD_BUTTON_UNPRESSED,
16645 		      GDI_CHECKED, checked,
16646 		      GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16647 		      GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16648 		      GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16649 		      GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16650 		      GDI_DIRECT_DRAW, FALSE,
16651 		      GDI_EVENT_MASK, event_mask,
16652 		      GDI_CALLBACK_ACTION, HandleGameButtons,
16653 		      GDI_END);
16654 
16655     if (gi == NULL)
16656       Error(ERR_EXIT, "cannot create gadget");
16657 
16658     game_gadget[id] = gi;
16659   }
16660 }
16661 
FreeGameButtons()16662 void FreeGameButtons()
16663 {
16664   int i;
16665 
16666   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16667     FreeGadget(game_gadget[i]);
16668 }
16669 
MapGameButtons()16670 static void MapGameButtons()
16671 {
16672   int i;
16673 
16674   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16675     MapGadget(game_gadget[i]);
16676 }
16677 
UnmapGameButtons()16678 void UnmapGameButtons()
16679 {
16680   int i;
16681 
16682   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16683     UnmapGadget(game_gadget[i]);
16684 }
16685 
RedrawGameButtons()16686 void RedrawGameButtons()
16687 {
16688   int i;
16689 
16690   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16691     RedrawGadget(game_gadget[i]);
16692 }
16693 
HandleGameButtonsExt(int id)16694 static void HandleGameButtonsExt(int id)
16695 {
16696   if (game_status != GAME_MODE_PLAYING)
16697     return;
16698 
16699   switch (id)
16700   {
16701     case GAME_CTRL_ID_STOP:
16702       if (tape.playing)
16703 	TapeStop();
16704       else
16705 	RequestQuitGame(TRUE);
16706       break;
16707 
16708     case GAME_CTRL_ID_PAUSE:
16709       if (options.network)
16710       {
16711 #if defined(NETWORK_AVALIABLE)
16712 	if (tape.pausing)
16713 	  SendToServer_ContinuePlaying();
16714 	else
16715 	  SendToServer_PausePlaying();
16716 #endif
16717       }
16718       else
16719 	TapeTogglePause(TAPE_TOGGLE_MANUAL);
16720       break;
16721 
16722     case GAME_CTRL_ID_PLAY:
16723       if (tape.pausing)
16724       {
16725 #if defined(NETWORK_AVALIABLE)
16726 	if (options.network)
16727 	  SendToServer_ContinuePlaying();
16728 	else
16729 #endif
16730 	{
16731 	  tape.pausing = FALSE;
16732 	  DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16733 	}
16734       }
16735       break;
16736 
16737     case SOUND_CTRL_ID_MUSIC:
16738       if (setup.sound_music)
16739       {
16740 	setup.sound_music = FALSE;
16741 
16742 	FadeMusic();
16743       }
16744       else if (audio.music_available)
16745       {
16746 	setup.sound = setup.sound_music = TRUE;
16747 
16748 	SetAudioMode(setup.sound);
16749 
16750 	PlayLevelMusic();
16751       }
16752       break;
16753 
16754     case SOUND_CTRL_ID_LOOPS:
16755       if (setup.sound_loops)
16756 	setup.sound_loops = FALSE;
16757       else if (audio.loops_available)
16758       {
16759 	setup.sound = setup.sound_loops = TRUE;
16760 
16761 	SetAudioMode(setup.sound);
16762       }
16763       break;
16764 
16765     case SOUND_CTRL_ID_SIMPLE:
16766       if (setup.sound_simple)
16767 	setup.sound_simple = FALSE;
16768       else if (audio.sound_available)
16769       {
16770 	setup.sound = setup.sound_simple = TRUE;
16771 
16772 	SetAudioMode(setup.sound);
16773       }
16774       break;
16775 
16776     default:
16777       break;
16778   }
16779 }
16780 
HandleGameButtons(struct GadgetInfo * gi)16781 static void HandleGameButtons(struct GadgetInfo *gi)
16782 {
16783   HandleGameButtonsExt(gi->custom_id);
16784 }
16785 
HandleSoundButtonKeys(Key key)16786 void HandleSoundButtonKeys(Key key)
16787 {
16788 #if 1
16789   if (key == setup.shortcut.sound_simple)
16790     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16791   else if (key == setup.shortcut.sound_loops)
16792     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16793   else if (key == setup.shortcut.sound_music)
16794     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16795 #else
16796   if (key == setup.shortcut.sound_simple)
16797     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
16798   else if (key == setup.shortcut.sound_loops)
16799     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
16800   else if (key == setup.shortcut.sound_music)
16801     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
16802 #endif
16803 }
16804