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 /* special positions in the game control window (relative to control window) */
124 #define XX_LEVEL1		(PANEL_XPOS(game.panel.level))
125 #define XX_LEVEL2		(PANEL_XPOS(game.panel.level) - 1)
126 #define XX_LEVEL		(PANEL_XPOS(game.panel.level))
127 #define YY_LEVEL		(PANEL_YPOS(game.panel.level))
128 #define XX_EMERALDS		(PANEL_XPOS(game.panel.gems))
129 #define YY_EMERALDS		(PANEL_YPOS(game.panel.gems))
130 #define XX_DYNAMITE		(PANEL_XPOS(game.panel.inventory))
131 #define YY_DYNAMITE		(PANEL_YPOS(game.panel.inventory))
132 #define XX_KEYS			(PANEL_XPOS(game.panel.keys))
133 #define YY_KEYS			(PANEL_YPOS(game.panel.keys))
134 #define XX_SCORE		(PANEL_XPOS(game.panel.score))
135 #define YY_SCORE		(PANEL_YPOS(game.panel.score))
136 #define XX_TIME1		(PANEL_XPOS(game.panel.time))
137 #define XX_TIME2		(PANEL_XPOS(game.panel.time) + 1)
138 #define XX_TIME			(PANEL_XPOS(game.panel.time))
139 #define YY_TIME			(PANEL_YPOS(game.panel.time))
140 
141 /* special positions in the game control window (relative to main window) */
142 #define DX_LEVEL1		(DX + XX_LEVEL1)
143 #define DX_LEVEL2		(DX + XX_LEVEL2)
144 #define DX_LEVEL		(DX + XX_LEVEL)
145 #define DY_LEVEL		(DY + YY_LEVEL)
146 #define DX_EMERALDS		(DX + XX_EMERALDS)
147 #define DY_EMERALDS		(DY + YY_EMERALDS)
148 #define DX_DYNAMITE		(DX + XX_DYNAMITE)
149 #define DY_DYNAMITE		(DY + YY_DYNAMITE)
150 #define DX_KEYS			(DX + XX_KEYS)
151 #define DY_KEYS			(DY + YY_KEYS)
152 #define DX_SCORE		(DX + XX_SCORE)
153 #define DY_SCORE		(DY + YY_SCORE)
154 #define DX_TIME1		(DX + XX_TIME1)
155 #define DX_TIME2		(DX + XX_TIME2)
156 #define DX_TIME			(DX + XX_TIME)
157 #define DY_TIME			(DY + YY_TIME)
158 
159 #if 1
160 /* game panel display and control definitions */
161 
162 #define GAME_PANEL_LEVEL_NUMBER			0
163 #define GAME_PANEL_GEMS				1
164 #define GAME_PANEL_INVENTORY_COUNT		2
165 #define GAME_PANEL_INVENTORY_FIRST_1		3
166 #define GAME_PANEL_INVENTORY_FIRST_2		4
167 #define GAME_PANEL_INVENTORY_FIRST_3		5
168 #define GAME_PANEL_INVENTORY_FIRST_4		6
169 #define GAME_PANEL_INVENTORY_FIRST_5		7
170 #define GAME_PANEL_INVENTORY_FIRST_6		8
171 #define GAME_PANEL_INVENTORY_FIRST_7		9
172 #define GAME_PANEL_INVENTORY_FIRST_8		10
173 #define GAME_PANEL_INVENTORY_LAST_1		11
174 #define GAME_PANEL_INVENTORY_LAST_2		12
175 #define GAME_PANEL_INVENTORY_LAST_3		13
176 #define GAME_PANEL_INVENTORY_LAST_4		14
177 #define GAME_PANEL_INVENTORY_LAST_5		15
178 #define GAME_PANEL_INVENTORY_LAST_6		16
179 #define GAME_PANEL_INVENTORY_LAST_7		17
180 #define GAME_PANEL_INVENTORY_LAST_8		18
181 #define GAME_PANEL_KEY_1			19
182 #define GAME_PANEL_KEY_2			20
183 #define GAME_PANEL_KEY_3			21
184 #define GAME_PANEL_KEY_4			22
185 #define GAME_PANEL_KEY_5			23
186 #define GAME_PANEL_KEY_6			24
187 #define GAME_PANEL_KEY_7			25
188 #define GAME_PANEL_KEY_8			26
189 #define GAME_PANEL_KEY_WHITE			27
190 #define GAME_PANEL_KEY_WHITE_COUNT		28
191 #define GAME_PANEL_SCORE			29
192 #define GAME_PANEL_HIGHSCORE			30
193 #define GAME_PANEL_TIME				31
194 #define GAME_PANEL_TIME_HH			32
195 #define GAME_PANEL_TIME_MM			33
196 #define GAME_PANEL_TIME_SS			34
197 #define GAME_PANEL_FRAME			35
198 #define GAME_PANEL_SHIELD_NORMAL		36
199 #define GAME_PANEL_SHIELD_NORMAL_TIME		37
200 #define GAME_PANEL_SHIELD_DEADLY		38
201 #define GAME_PANEL_SHIELD_DEADLY_TIME		39
202 #define GAME_PANEL_EXIT				40
203 #define GAME_PANEL_EMC_MAGIC_BALL		41
204 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH	42
205 #define GAME_PANEL_LIGHT_SWITCH			43
206 #define GAME_PANEL_LIGHT_SWITCH_TIME		44
207 #define GAME_PANEL_TIMEGATE_SWITCH		45
208 #define GAME_PANEL_TIMEGATE_SWITCH_TIME		46
209 #define GAME_PANEL_SWITCHGATE_SWITCH		47
210 #define GAME_PANEL_EMC_LENSES			48
211 #define GAME_PANEL_EMC_LENSES_TIME		49
212 #define GAME_PANEL_EMC_MAGNIFIER		50
213 #define GAME_PANEL_EMC_MAGNIFIER_TIME		51
214 #define GAME_PANEL_BALLOON_SWITCH		52
215 #define GAME_PANEL_DYNABOMB_NUMBER		53
216 #define GAME_PANEL_DYNABOMB_SIZE		54
217 #define GAME_PANEL_DYNABOMB_POWER		55
218 #define GAME_PANEL_PENGUINS			56
219 #define GAME_PANEL_SOKOBAN_OBJECTS		57
220 #define GAME_PANEL_SOKOBAN_FIELDS		58
221 #define GAME_PANEL_ROBOT_WHEEL			59
222 #define GAME_PANEL_CONVEYOR_BELT_1		60
223 #define GAME_PANEL_CONVEYOR_BELT_2		61
224 #define GAME_PANEL_CONVEYOR_BELT_3		62
225 #define GAME_PANEL_CONVEYOR_BELT_4		63
226 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH	64
227 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH	65
228 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH	66
229 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH	67
230 #define GAME_PANEL_MAGIC_WALL			68
231 #define GAME_PANEL_MAGIC_WALL_TIME		69
232 #define GAME_PANEL_GRAVITY_STATE		70
233 #define GAME_PANEL_GRAPHIC_1			71
234 #define GAME_PANEL_GRAPHIC_2			72
235 #define GAME_PANEL_GRAPHIC_3			73
236 #define GAME_PANEL_GRAPHIC_4			74
237 #define GAME_PANEL_GRAPHIC_5			75
238 #define GAME_PANEL_GRAPHIC_6			76
239 #define GAME_PANEL_GRAPHIC_7			77
240 #define GAME_PANEL_GRAPHIC_8			78
241 #define GAME_PANEL_ELEMENT_1			79
242 #define GAME_PANEL_ELEMENT_2			80
243 #define GAME_PANEL_ELEMENT_3			81
244 #define GAME_PANEL_ELEMENT_4			82
245 #define GAME_PANEL_ELEMENT_5			83
246 #define GAME_PANEL_ELEMENT_6			84
247 #define GAME_PANEL_ELEMENT_7			85
248 #define GAME_PANEL_ELEMENT_8			86
249 #define GAME_PANEL_ELEMENT_COUNT_1		87
250 #define GAME_PANEL_ELEMENT_COUNT_2		88
251 #define GAME_PANEL_ELEMENT_COUNT_3		89
252 #define GAME_PANEL_ELEMENT_COUNT_4		90
253 #define GAME_PANEL_ELEMENT_COUNT_5		91
254 #define GAME_PANEL_ELEMENT_COUNT_6		92
255 #define GAME_PANEL_ELEMENT_COUNT_7		93
256 #define GAME_PANEL_ELEMENT_COUNT_8		94
257 #define GAME_PANEL_CE_SCORE_1			95
258 #define GAME_PANEL_CE_SCORE_2			96
259 #define GAME_PANEL_CE_SCORE_3			97
260 #define GAME_PANEL_CE_SCORE_4			98
261 #define GAME_PANEL_CE_SCORE_5			99
262 #define GAME_PANEL_CE_SCORE_6			100
263 #define GAME_PANEL_CE_SCORE_7			101
264 #define GAME_PANEL_CE_SCORE_8			102
265 #define GAME_PANEL_CE_SCORE_1_ELEMENT		103
266 #define GAME_PANEL_CE_SCORE_2_ELEMENT		104
267 #define GAME_PANEL_CE_SCORE_3_ELEMENT		105
268 #define GAME_PANEL_CE_SCORE_4_ELEMENT		106
269 #define GAME_PANEL_CE_SCORE_5_ELEMENT		107
270 #define GAME_PANEL_CE_SCORE_6_ELEMENT		108
271 #define GAME_PANEL_CE_SCORE_7_ELEMENT		109
272 #define GAME_PANEL_CE_SCORE_8_ELEMENT		110
273 #define GAME_PANEL_PLAYER_NAME			111
274 #define GAME_PANEL_LEVEL_NAME			112
275 #define GAME_PANEL_LEVEL_AUTHOR			113
276 
277 #define NUM_GAME_PANEL_CONTROLS			114
278 
279 struct GamePanelOrderInfo
280 {
281   int nr;
282   int sort_priority;
283 };
284 
285 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286 
287 struct GamePanelControlInfo
288 {
289   int nr;
290 
291   struct TextPosInfo *pos;
292   int type;
293 
294   int value, last_value;
295   int frame, last_frame;
296   int gfx_frame;
297   int gfx_random;
298 };
299 
300 static struct GamePanelControlInfo game_panel_controls[] =
301 {
302   {
303     GAME_PANEL_LEVEL_NUMBER,
304     &game.panel.level_number,
305     TYPE_INTEGER,
306   },
307   {
308     GAME_PANEL_GEMS,
309     &game.panel.gems,
310     TYPE_INTEGER,
311   },
312   {
313     GAME_PANEL_INVENTORY_COUNT,
314     &game.panel.inventory_count,
315     TYPE_INTEGER,
316   },
317   {
318     GAME_PANEL_INVENTORY_FIRST_1,
319     &game.panel.inventory_first[0],
320     TYPE_ELEMENT,
321   },
322   {
323     GAME_PANEL_INVENTORY_FIRST_2,
324     &game.panel.inventory_first[1],
325     TYPE_ELEMENT,
326   },
327   {
328     GAME_PANEL_INVENTORY_FIRST_3,
329     &game.panel.inventory_first[2],
330     TYPE_ELEMENT,
331   },
332   {
333     GAME_PANEL_INVENTORY_FIRST_4,
334     &game.panel.inventory_first[3],
335     TYPE_ELEMENT,
336   },
337   {
338     GAME_PANEL_INVENTORY_FIRST_5,
339     &game.panel.inventory_first[4],
340     TYPE_ELEMENT,
341   },
342   {
343     GAME_PANEL_INVENTORY_FIRST_6,
344     &game.panel.inventory_first[5],
345     TYPE_ELEMENT,
346   },
347   {
348     GAME_PANEL_INVENTORY_FIRST_7,
349     &game.panel.inventory_first[6],
350     TYPE_ELEMENT,
351   },
352   {
353     GAME_PANEL_INVENTORY_FIRST_8,
354     &game.panel.inventory_first[7],
355     TYPE_ELEMENT,
356   },
357   {
358     GAME_PANEL_INVENTORY_LAST_1,
359     &game.panel.inventory_last[0],
360     TYPE_ELEMENT,
361   },
362   {
363     GAME_PANEL_INVENTORY_LAST_2,
364     &game.panel.inventory_last[1],
365     TYPE_ELEMENT,
366   },
367   {
368     GAME_PANEL_INVENTORY_LAST_3,
369     &game.panel.inventory_last[2],
370     TYPE_ELEMENT,
371   },
372   {
373     GAME_PANEL_INVENTORY_LAST_4,
374     &game.panel.inventory_last[3],
375     TYPE_ELEMENT,
376   },
377   {
378     GAME_PANEL_INVENTORY_LAST_5,
379     &game.panel.inventory_last[4],
380     TYPE_ELEMENT,
381   },
382   {
383     GAME_PANEL_INVENTORY_LAST_6,
384     &game.panel.inventory_last[5],
385     TYPE_ELEMENT,
386   },
387   {
388     GAME_PANEL_INVENTORY_LAST_7,
389     &game.panel.inventory_last[6],
390     TYPE_ELEMENT,
391   },
392   {
393     GAME_PANEL_INVENTORY_LAST_8,
394     &game.panel.inventory_last[7],
395     TYPE_ELEMENT,
396   },
397   {
398     GAME_PANEL_KEY_1,
399     &game.panel.key[0],
400     TYPE_ELEMENT,
401   },
402   {
403     GAME_PANEL_KEY_2,
404     &game.panel.key[1],
405     TYPE_ELEMENT,
406   },
407   {
408     GAME_PANEL_KEY_3,
409     &game.panel.key[2],
410     TYPE_ELEMENT,
411   },
412   {
413     GAME_PANEL_KEY_4,
414     &game.panel.key[3],
415     TYPE_ELEMENT,
416   },
417   {
418     GAME_PANEL_KEY_5,
419     &game.panel.key[4],
420     TYPE_ELEMENT,
421   },
422   {
423     GAME_PANEL_KEY_6,
424     &game.panel.key[5],
425     TYPE_ELEMENT,
426   },
427   {
428     GAME_PANEL_KEY_7,
429     &game.panel.key[6],
430     TYPE_ELEMENT,
431   },
432   {
433     GAME_PANEL_KEY_8,
434     &game.panel.key[7],
435     TYPE_ELEMENT,
436   },
437   {
438     GAME_PANEL_KEY_WHITE,
439     &game.panel.key_white,
440     TYPE_ELEMENT,
441   },
442   {
443     GAME_PANEL_KEY_WHITE_COUNT,
444     &game.panel.key_white_count,
445     TYPE_INTEGER,
446   },
447   {
448     GAME_PANEL_SCORE,
449     &game.panel.score,
450     TYPE_INTEGER,
451   },
452   {
453     GAME_PANEL_HIGHSCORE,
454     &game.panel.highscore,
455     TYPE_INTEGER,
456   },
457   {
458     GAME_PANEL_TIME,
459     &game.panel.time,
460     TYPE_INTEGER,
461   },
462   {
463     GAME_PANEL_TIME_HH,
464     &game.panel.time_hh,
465     TYPE_INTEGER,
466   },
467   {
468     GAME_PANEL_TIME_MM,
469     &game.panel.time_mm,
470     TYPE_INTEGER,
471   },
472   {
473     GAME_PANEL_TIME_SS,
474     &game.panel.time_ss,
475     TYPE_INTEGER,
476   },
477   {
478     GAME_PANEL_FRAME,
479     &game.panel.frame,
480     TYPE_INTEGER,
481   },
482   {
483     GAME_PANEL_SHIELD_NORMAL,
484     &game.panel.shield_normal,
485     TYPE_ELEMENT,
486   },
487   {
488     GAME_PANEL_SHIELD_NORMAL_TIME,
489     &game.panel.shield_normal_time,
490     TYPE_INTEGER,
491   },
492   {
493     GAME_PANEL_SHIELD_DEADLY,
494     &game.panel.shield_deadly,
495     TYPE_ELEMENT,
496   },
497   {
498     GAME_PANEL_SHIELD_DEADLY_TIME,
499     &game.panel.shield_deadly_time,
500     TYPE_INTEGER,
501   },
502   {
503     GAME_PANEL_EXIT,
504     &game.panel.exit,
505     TYPE_ELEMENT,
506   },
507   {
508     GAME_PANEL_EMC_MAGIC_BALL,
509     &game.panel.emc_magic_ball,
510     TYPE_ELEMENT,
511   },
512   {
513     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
514     &game.panel.emc_magic_ball_switch,
515     TYPE_ELEMENT,
516   },
517   {
518     GAME_PANEL_LIGHT_SWITCH,
519     &game.panel.light_switch,
520     TYPE_ELEMENT,
521   },
522   {
523     GAME_PANEL_LIGHT_SWITCH_TIME,
524     &game.panel.light_switch_time,
525     TYPE_INTEGER,
526   },
527   {
528     GAME_PANEL_TIMEGATE_SWITCH,
529     &game.panel.timegate_switch,
530     TYPE_ELEMENT,
531   },
532   {
533     GAME_PANEL_TIMEGATE_SWITCH_TIME,
534     &game.panel.timegate_switch_time,
535     TYPE_INTEGER,
536   },
537   {
538     GAME_PANEL_SWITCHGATE_SWITCH,
539     &game.panel.switchgate_switch,
540     TYPE_ELEMENT,
541   },
542   {
543     GAME_PANEL_EMC_LENSES,
544     &game.panel.emc_lenses,
545     TYPE_ELEMENT,
546   },
547   {
548     GAME_PANEL_EMC_LENSES_TIME,
549     &game.panel.emc_lenses_time,
550     TYPE_INTEGER,
551   },
552   {
553     GAME_PANEL_EMC_MAGNIFIER,
554     &game.panel.emc_magnifier,
555     TYPE_ELEMENT,
556   },
557   {
558     GAME_PANEL_EMC_MAGNIFIER_TIME,
559     &game.panel.emc_magnifier_time,
560     TYPE_INTEGER,
561   },
562   {
563     GAME_PANEL_BALLOON_SWITCH,
564     &game.panel.balloon_switch,
565     TYPE_ELEMENT,
566   },
567   {
568     GAME_PANEL_DYNABOMB_NUMBER,
569     &game.panel.dynabomb_number,
570     TYPE_INTEGER,
571   },
572   {
573     GAME_PANEL_DYNABOMB_SIZE,
574     &game.panel.dynabomb_size,
575     TYPE_INTEGER,
576   },
577   {
578     GAME_PANEL_DYNABOMB_POWER,
579     &game.panel.dynabomb_power,
580     TYPE_ELEMENT,
581   },
582   {
583     GAME_PANEL_PENGUINS,
584     &game.panel.penguins,
585     TYPE_INTEGER,
586   },
587   {
588     GAME_PANEL_SOKOBAN_OBJECTS,
589     &game.panel.sokoban_objects,
590     TYPE_INTEGER,
591   },
592   {
593     GAME_PANEL_SOKOBAN_FIELDS,
594     &game.panel.sokoban_fields,
595     TYPE_INTEGER,
596   },
597   {
598     GAME_PANEL_ROBOT_WHEEL,
599     &game.panel.robot_wheel,
600     TYPE_ELEMENT,
601   },
602   {
603     GAME_PANEL_CONVEYOR_BELT_1,
604     &game.panel.conveyor_belt[0],
605     TYPE_ELEMENT,
606   },
607   {
608     GAME_PANEL_CONVEYOR_BELT_2,
609     &game.panel.conveyor_belt[1],
610     TYPE_ELEMENT,
611   },
612   {
613     GAME_PANEL_CONVEYOR_BELT_3,
614     &game.panel.conveyor_belt[2],
615     TYPE_ELEMENT,
616   },
617   {
618     GAME_PANEL_CONVEYOR_BELT_4,
619     &game.panel.conveyor_belt[3],
620     TYPE_ELEMENT,
621   },
622   {
623     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
624     &game.panel.conveyor_belt_switch[0],
625     TYPE_ELEMENT,
626   },
627   {
628     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
629     &game.panel.conveyor_belt_switch[1],
630     TYPE_ELEMENT,
631   },
632   {
633     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
634     &game.panel.conveyor_belt_switch[2],
635     TYPE_ELEMENT,
636   },
637   {
638     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
639     &game.panel.conveyor_belt_switch[3],
640     TYPE_ELEMENT,
641   },
642   {
643     GAME_PANEL_MAGIC_WALL,
644     &game.panel.magic_wall,
645     TYPE_ELEMENT,
646   },
647   {
648     GAME_PANEL_MAGIC_WALL_TIME,
649     &game.panel.magic_wall_time,
650     TYPE_INTEGER,
651   },
652   {
653     GAME_PANEL_GRAVITY_STATE,
654     &game.panel.gravity_state,
655     TYPE_STRING,
656   },
657   {
658     GAME_PANEL_GRAPHIC_1,
659     &game.panel.graphic[0],
660     TYPE_ELEMENT,
661   },
662   {
663     GAME_PANEL_GRAPHIC_2,
664     &game.panel.graphic[1],
665     TYPE_ELEMENT,
666   },
667   {
668     GAME_PANEL_GRAPHIC_3,
669     &game.panel.graphic[2],
670     TYPE_ELEMENT,
671   },
672   {
673     GAME_PANEL_GRAPHIC_4,
674     &game.panel.graphic[3],
675     TYPE_ELEMENT,
676   },
677   {
678     GAME_PANEL_GRAPHIC_5,
679     &game.panel.graphic[4],
680     TYPE_ELEMENT,
681   },
682   {
683     GAME_PANEL_GRAPHIC_6,
684     &game.panel.graphic[5],
685     TYPE_ELEMENT,
686   },
687   {
688     GAME_PANEL_GRAPHIC_7,
689     &game.panel.graphic[6],
690     TYPE_ELEMENT,
691   },
692   {
693     GAME_PANEL_GRAPHIC_8,
694     &game.panel.graphic[7],
695     TYPE_ELEMENT,
696   },
697   {
698     GAME_PANEL_ELEMENT_1,
699     &game.panel.element[0],
700     TYPE_ELEMENT,
701   },
702   {
703     GAME_PANEL_ELEMENT_2,
704     &game.panel.element[1],
705     TYPE_ELEMENT,
706   },
707   {
708     GAME_PANEL_ELEMENT_3,
709     &game.panel.element[2],
710     TYPE_ELEMENT,
711   },
712   {
713     GAME_PANEL_ELEMENT_4,
714     &game.panel.element[3],
715     TYPE_ELEMENT,
716   },
717   {
718     GAME_PANEL_ELEMENT_5,
719     &game.panel.element[4],
720     TYPE_ELEMENT,
721   },
722   {
723     GAME_PANEL_ELEMENT_6,
724     &game.panel.element[5],
725     TYPE_ELEMENT,
726   },
727   {
728     GAME_PANEL_ELEMENT_7,
729     &game.panel.element[6],
730     TYPE_ELEMENT,
731   },
732   {
733     GAME_PANEL_ELEMENT_8,
734     &game.panel.element[7],
735     TYPE_ELEMENT,
736   },
737   {
738     GAME_PANEL_ELEMENT_COUNT_1,
739     &game.panel.element_count[0],
740     TYPE_INTEGER,
741   },
742   {
743     GAME_PANEL_ELEMENT_COUNT_2,
744     &game.panel.element_count[1],
745     TYPE_INTEGER,
746   },
747   {
748     GAME_PANEL_ELEMENT_COUNT_3,
749     &game.panel.element_count[2],
750     TYPE_INTEGER,
751   },
752   {
753     GAME_PANEL_ELEMENT_COUNT_4,
754     &game.panel.element_count[3],
755     TYPE_INTEGER,
756   },
757   {
758     GAME_PANEL_ELEMENT_COUNT_5,
759     &game.panel.element_count[4],
760     TYPE_INTEGER,
761   },
762   {
763     GAME_PANEL_ELEMENT_COUNT_6,
764     &game.panel.element_count[5],
765     TYPE_INTEGER,
766   },
767   {
768     GAME_PANEL_ELEMENT_COUNT_7,
769     &game.panel.element_count[6],
770     TYPE_INTEGER,
771   },
772   {
773     GAME_PANEL_ELEMENT_COUNT_8,
774     &game.panel.element_count[7],
775     TYPE_INTEGER,
776   },
777   {
778     GAME_PANEL_CE_SCORE_1,
779     &game.panel.ce_score[0],
780     TYPE_INTEGER,
781   },
782   {
783     GAME_PANEL_CE_SCORE_2,
784     &game.panel.ce_score[1],
785     TYPE_INTEGER,
786   },
787   {
788     GAME_PANEL_CE_SCORE_3,
789     &game.panel.ce_score[2],
790     TYPE_INTEGER,
791   },
792   {
793     GAME_PANEL_CE_SCORE_4,
794     &game.panel.ce_score[3],
795     TYPE_INTEGER,
796   },
797   {
798     GAME_PANEL_CE_SCORE_5,
799     &game.panel.ce_score[4],
800     TYPE_INTEGER,
801   },
802   {
803     GAME_PANEL_CE_SCORE_6,
804     &game.panel.ce_score[5],
805     TYPE_INTEGER,
806   },
807   {
808     GAME_PANEL_CE_SCORE_7,
809     &game.panel.ce_score[6],
810     TYPE_INTEGER,
811   },
812   {
813     GAME_PANEL_CE_SCORE_8,
814     &game.panel.ce_score[7],
815     TYPE_INTEGER,
816   },
817   {
818     GAME_PANEL_CE_SCORE_1_ELEMENT,
819     &game.panel.ce_score_element[0],
820     TYPE_ELEMENT,
821   },
822   {
823     GAME_PANEL_CE_SCORE_2_ELEMENT,
824     &game.panel.ce_score_element[1],
825     TYPE_ELEMENT,
826   },
827   {
828     GAME_PANEL_CE_SCORE_3_ELEMENT,
829     &game.panel.ce_score_element[2],
830     TYPE_ELEMENT,
831   },
832   {
833     GAME_PANEL_CE_SCORE_4_ELEMENT,
834     &game.panel.ce_score_element[3],
835     TYPE_ELEMENT,
836   },
837   {
838     GAME_PANEL_CE_SCORE_5_ELEMENT,
839     &game.panel.ce_score_element[4],
840     TYPE_ELEMENT,
841   },
842   {
843     GAME_PANEL_CE_SCORE_6_ELEMENT,
844     &game.panel.ce_score_element[5],
845     TYPE_ELEMENT,
846   },
847   {
848     GAME_PANEL_CE_SCORE_7_ELEMENT,
849     &game.panel.ce_score_element[6],
850     TYPE_ELEMENT,
851   },
852   {
853     GAME_PANEL_CE_SCORE_8_ELEMENT,
854     &game.panel.ce_score_element[7],
855     TYPE_ELEMENT,
856   },
857   {
858     GAME_PANEL_PLAYER_NAME,
859     &game.panel.player_name,
860     TYPE_STRING,
861   },
862   {
863     GAME_PANEL_LEVEL_NAME,
864     &game.panel.level_name,
865     TYPE_STRING,
866   },
867   {
868     GAME_PANEL_LEVEL_AUTHOR,
869     &game.panel.level_author,
870     TYPE_STRING,
871   },
872 
873   {
874     -1,
875     NULL,
876     -1,
877   }
878 };
879 #endif
880 
881 
882 /* values for delayed check of falling and moving elements and for collision */
883 #define CHECK_DELAY_MOVING	3
884 #define CHECK_DELAY_FALLING	CHECK_DELAY_MOVING
885 #define CHECK_DELAY_COLLISION	2
886 #define CHECK_DELAY_IMPACT	CHECK_DELAY_COLLISION
887 
888 /* values for initial player move delay (initial delay counter value) */
889 #define INITIAL_MOVE_DELAY_OFF	-1
890 #define INITIAL_MOVE_DELAY_ON	0
891 
892 /* values for player movement speed (which is in fact a delay value) */
893 #define MOVE_DELAY_MIN_SPEED	32
894 #define MOVE_DELAY_NORMAL_SPEED	8
895 #define MOVE_DELAY_HIGH_SPEED	4
896 #define MOVE_DELAY_MAX_SPEED	1
897 
898 #define DOUBLE_MOVE_DELAY(x)	(x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
899 #define HALVE_MOVE_DELAY(x)	(x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
900 
901 #define DOUBLE_PLAYER_SPEED(p)	(HALVE_MOVE_DELAY( (p)->move_delay_value))
902 #define HALVE_PLAYER_SPEED(p)	(DOUBLE_MOVE_DELAY((p)->move_delay_value))
903 
904 /* values for other actions */
905 #define MOVE_STEPSIZE_NORMAL	(TILEX / MOVE_DELAY_NORMAL_SPEED)
906 #define MOVE_STEPSIZE_MIN	(1)
907 #define MOVE_STEPSIZE_MAX	(TILEX)
908 
909 #define GET_DX_FROM_DIR(d)	((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
910 #define GET_DY_FROM_DIR(d)	((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
911 
912 #define	INIT_GFX_RANDOM()	(GetSimpleRandom(1000000))
913 
914 #define GET_NEW_PUSH_DELAY(e)	(   (element_info[e].push_delay_fixed) + \
915 				 RND(element_info[e].push_delay_random))
916 #define GET_NEW_DROP_DELAY(e)	(   (element_info[e].drop_delay_fixed) + \
917 				 RND(element_info[e].drop_delay_random))
918 #define GET_NEW_MOVE_DELAY(e)	(   (element_info[e].move_delay_fixed) + \
919 				 RND(element_info[e].move_delay_random))
920 #define GET_MAX_MOVE_DELAY(e)	(   (element_info[e].move_delay_fixed) + \
921 				    (element_info[e].move_delay_random))
922 #define GET_NEW_CE_VALUE(e)	(   (element_info[e].ce_value_fixed_initial) +\
923 				 RND(element_info[e].ce_value_random_initial))
924 #define GET_CE_SCORE(e)		(   (element_info[e].collect_score))
925 #define GET_CHANGE_DELAY(c)	(   ((c)->delay_fixed  * (c)->delay_frames) + \
926 				 RND((c)->delay_random * (c)->delay_frames))
927 #define GET_CE_DELAY_VALUE(c)	(   ((c)->delay_fixed) + \
928 				 RND((c)->delay_random))
929 
930 
931 #define GET_VALID_RUNTIME_ELEMENT(e)					\
932 	 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
933 
934 #define RESOLVED_REFERENCE_ELEMENT(be, e)				\
935 	((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :	\
936 	 (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :	\
937 	 (be) + (e) - EL_SELF)
938 
939 #define GET_PLAYER_FROM_BITS(p)						\
940 	(EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
941 
942 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)				\
943 	((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :	\
944 	 (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :	\
945 	 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :	\
946 	 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :	\
947 	 (e) == EL_CURRENT_CE_VALUE ? (cv) :				\
948 	 (e) == EL_CURRENT_CE_SCORE ? (cs) :				\
949 	 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?			\
950 	 RESOLVED_REFERENCE_ELEMENT(be, e) :				\
951 	 (e))
952 
953 #define CAN_GROW_INTO(e)						\
954 	((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
955 
956 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)			\
957 		(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||		\
958 					(condition)))
959 
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)		\
961 		(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||		\
962 					(CAN_MOVE_INTO_ACID(e) &&	\
963 					 Feld[x][y] == EL_ACID) ||	\
964 					(condition)))
965 
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)		\
967 		(IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||	\
968 					(CAN_MOVE_INTO_ACID(e) &&	\
969 					 Feld[x][y] == EL_ACID) ||	\
970 					(condition)))
971 
972 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)		\
973 		(IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||		\
974 					(condition) ||			\
975 					(CAN_MOVE_INTO_ACID(e) &&	\
976 					 Feld[x][y] == EL_ACID) ||	\
977 					(DONT_COLLIDE_WITH(e) &&	\
978 					 IS_PLAYER(x, y) &&		\
979 					 !PLAYER_ENEMY_PROTECTED(x, y))))
980 
981 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)				\
982 	ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
983 
984 #define SATELLITE_CAN_ENTER_FIELD(x, y)					\
985 	ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
986 
987 #define ANDROID_CAN_ENTER_FIELD(e, x, y)				\
988 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
989 
990 #define ANDROID_CAN_CLONE_FIELD(x, y)					\
991 	(IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) ||	\
992 				CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
993 
994 #define ENEMY_CAN_ENTER_FIELD(e, x, y)					\
995 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
996 
997 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)					\
998 	ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
999 
1000 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)				\
1001 	ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
1002 
1003 #define PACMAN_CAN_ENTER_FIELD(e, x, y)					\
1004 	ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1005 
1006 #define PIG_CAN_ENTER_FIELD(e, x, y)					\
1007 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1008 
1009 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)				\
1010 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1011 						 Feld[x][y] == EL_EM_EXIT_OPEN || \
1012 						 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1013 						 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1014 						 IS_FOOD_PENGUIN(Feld[x][y])))
1015 #define DRAGON_CAN_ENTER_FIELD(e, x, y)					\
1016 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1017 
1018 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)			\
1019 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1020 
1021 #define SPRING_CAN_ENTER_FIELD(e, x, y)					\
1022 	ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1023 
1024 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)				\
1025 	(IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||	\
1026 				Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1027 
1028 #define MOVE_ENTER_EL(e)	(element_info[e].move_enter_element)
1029 
1030 #define CE_ENTER_FIELD_COND(e, x, y)					\
1031 		(!IS_PLAYER(x, y) &&					\
1032 		 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1033 
1034 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)				\
1035 	ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1036 
1037 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1038 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1039 
1040 #define ACCESS_FROM(e, d)		(element_info[e].access_direction &(d))
1041 #define IS_WALKABLE_FROM(e, d)		(IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1042 #define IS_PASSABLE_FROM(e, d)		(IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1043 #define IS_ACCESSIBLE_FROM(e, d)	(IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1044 
1045 /* game button identifiers */
1046 #define GAME_CTRL_ID_STOP		0
1047 #define GAME_CTRL_ID_PAUSE		1
1048 #define GAME_CTRL_ID_PLAY		2
1049 #define SOUND_CTRL_ID_MUSIC		3
1050 #define SOUND_CTRL_ID_LOOPS		4
1051 #define SOUND_CTRL_ID_SIMPLE		5
1052 
1053 #define NUM_GAME_BUTTONS		6
1054 
1055 
1056 /* forward declaration for internal use */
1057 
1058 static void CreateField(int, int, int);
1059 
1060 static void ResetGfxAnimation(int, int);
1061 
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064 
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069 
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074 
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081 
1082 static void TestIfPlayerTouchesCustomElement(int, int);
1083 static void TestIfElementTouchesCustomElement(int, int);
1084 static void TestIfElementHitsCustomElement(int, int, int);
1085 #if 0
1086 static void TestIfElementSmashesCustomElement(int, int, int);
1087 #endif
1088 
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092 
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)			\
1095 	CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)		\
1097 	CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)		\
1099 	CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)		\
1101 	CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 
1103 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1104 #define CheckElementChange(x, y, e, te, ev)				\
1105 	CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1106 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)			\
1107 	CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1108 #define CheckElementChangeBySide(x, y, e, te, ev, s)			\
1109 	CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1110 
1111 static void PlayLevelSound(int, int, int);
1112 static void PlayLevelSoundNearest(int, int, int);
1113 static void PlayLevelSoundAction(int, int, int);
1114 static void PlayLevelSoundElementAction(int, int, int, int);
1115 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1116 static void PlayLevelSoundActionIfLoop(int, int, int);
1117 static void StopLevelSoundActionIfLoop(int, int, int);
1118 static void PlayLevelMusic();
1119 
1120 static void MapGameButtons();
1121 static void HandleGameButtons(struct GadgetInfo *);
1122 
1123 int AmoebeNachbarNr(int, int);
1124 void AmoebeUmwandeln(int, int);
1125 void ContinueMoving(int, int);
1126 void Bang(int, int);
1127 void InitMovDir(int, int);
1128 void InitAmoebaNr(int, int);
1129 int NewHiScore(void);
1130 
1131 void TestIfGoodThingHitsBadThing(int, int, int);
1132 void TestIfBadThingHitsGoodThing(int, int, int);
1133 void TestIfPlayerTouchesBadThing(int, int);
1134 void TestIfPlayerRunsIntoBadThing(int, int, int);
1135 void TestIfBadThingTouchesPlayer(int, int);
1136 void TestIfBadThingRunsIntoPlayer(int, int, int);
1137 void TestIfFriendTouchesBadThing(int, int);
1138 void TestIfBadThingTouchesFriend(int, int);
1139 void TestIfBadThingTouchesOtherBadThing(int, int);
1140 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1141 
1142 void KillPlayer(struct PlayerInfo *);
1143 void BuryPlayer(struct PlayerInfo *);
1144 void RemovePlayer(struct PlayerInfo *);
1145 
1146 static int getInvisibleActiveFromInvisibleElement(int);
1147 static int getInvisibleFromInvisibleActiveElement(int);
1148 
1149 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1150 
1151 /* for detection of endless loops, caused by custom element programming */
1152 /* (using maximal playfield width x 10 is just a rough approximation) */
1153 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH	(MAX_PLAYFIELD_WIDTH * 10)
1154 
1155 #define RECURSION_LOOP_DETECTION_START(e, rc)				\
1156 {									\
1157   if (recursion_loop_detected)						\
1158     return (rc);							\
1159 									\
1160   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)	\
1161   {									\
1162     recursion_loop_detected = TRUE;					\
1163     recursion_loop_element = (e);					\
1164   }									\
1165 									\
1166   recursion_loop_depth++;						\
1167 }
1168 
1169 #define RECURSION_LOOP_DETECTION_END()					\
1170 {									\
1171   recursion_loop_depth--;						\
1172 }
1173 
1174 static int recursion_loop_depth;
1175 static boolean recursion_loop_detected;
1176 static boolean recursion_loop_element;
1177 
1178 static int map_player_action[MAX_PLAYERS];
1179 
1180 
1181 /* ------------------------------------------------------------------------- */
1182 /* definition of elements that automatically change to other elements after  */
1183 /* a specified time, eventually calling a function when changing             */
1184 /* ------------------------------------------------------------------------- */
1185 
1186 /* forward declaration for changer functions */
1187 static void InitBuggyBase(int, int);
1188 static void WarnBuggyBase(int, int);
1189 
1190 static void InitTrap(int, int);
1191 static void ActivateTrap(int, int);
1192 static void ChangeActiveTrap(int, int);
1193 
1194 static void InitRobotWheel(int, int);
1195 static void RunRobotWheel(int, int);
1196 static void StopRobotWheel(int, int);
1197 
1198 static void InitTimegateWheel(int, int);
1199 static void RunTimegateWheel(int, int);
1200 
1201 static void InitMagicBallDelay(int, int);
1202 static void ActivateMagicBall(int, int);
1203 
1204 struct ChangingElementInfo
1205 {
1206   int element;
1207   int target_element;
1208   int change_delay;
1209   void (*pre_change_function)(int x, int y);
1210   void (*change_function)(int x, int y);
1211   void (*post_change_function)(int x, int y);
1212 };
1213 
1214 static struct ChangingElementInfo change_delay_list[] =
1215 {
1216   {
1217     EL_NUT_BREAKING,
1218     EL_EMERALD,
1219     6,
1220     NULL,
1221     NULL,
1222     NULL
1223   },
1224   {
1225     EL_PEARL_BREAKING,
1226     EL_EMPTY,
1227     8,
1228     NULL,
1229     NULL,
1230     NULL
1231   },
1232   {
1233     EL_EXIT_OPENING,
1234     EL_EXIT_OPEN,
1235     29,
1236     NULL,
1237     NULL,
1238     NULL
1239   },
1240   {
1241     EL_EXIT_CLOSING,
1242     EL_EXIT_CLOSED,
1243     29,
1244     NULL,
1245     NULL,
1246     NULL
1247   },
1248   {
1249     EL_STEEL_EXIT_OPENING,
1250     EL_STEEL_EXIT_OPEN,
1251     29,
1252     NULL,
1253     NULL,
1254     NULL
1255   },
1256   {
1257     EL_STEEL_EXIT_CLOSING,
1258     EL_STEEL_EXIT_CLOSED,
1259     29,
1260     NULL,
1261     NULL,
1262     NULL
1263   },
1264   {
1265     EL_EM_EXIT_OPENING,
1266     EL_EM_EXIT_OPEN,
1267     29,
1268     NULL,
1269     NULL,
1270     NULL
1271   },
1272   {
1273     EL_EM_EXIT_CLOSING,
1274 #if 1
1275     EL_EMPTY,
1276 #else
1277     EL_EM_EXIT_CLOSED,
1278 #endif
1279     29,
1280     NULL,
1281     NULL,
1282     NULL
1283   },
1284   {
1285     EL_EM_STEEL_EXIT_OPENING,
1286     EL_EM_STEEL_EXIT_OPEN,
1287     29,
1288     NULL,
1289     NULL,
1290     NULL
1291   },
1292   {
1293     EL_EM_STEEL_EXIT_CLOSING,
1294 #if 1
1295     EL_STEELWALL,
1296 #else
1297     EL_EM_STEEL_EXIT_CLOSED,
1298 #endif
1299     29,
1300     NULL,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_EXIT_OPENING,
1306     EL_SP_EXIT_OPEN,
1307     29,
1308     NULL,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_EXIT_CLOSING,
1314     EL_SP_EXIT_CLOSED,
1315     29,
1316     NULL,
1317     NULL,
1318     NULL
1319   },
1320   {
1321     EL_SWITCHGATE_OPENING,
1322     EL_SWITCHGATE_OPEN,
1323     29,
1324     NULL,
1325     NULL,
1326     NULL
1327   },
1328   {
1329     EL_SWITCHGATE_CLOSING,
1330     EL_SWITCHGATE_CLOSED,
1331     29,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_TIMEGATE_OPENING,
1338     EL_TIMEGATE_OPEN,
1339     29,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_TIMEGATE_CLOSING,
1346     EL_TIMEGATE_CLOSED,
1347     29,
1348     NULL,
1349     NULL,
1350     NULL
1351   },
1352 
1353   {
1354     EL_ACID_SPLASH_LEFT,
1355     EL_EMPTY,
1356     8,
1357     NULL,
1358     NULL,
1359     NULL
1360   },
1361   {
1362     EL_ACID_SPLASH_RIGHT,
1363     EL_EMPTY,
1364     8,
1365     NULL,
1366     NULL,
1367     NULL
1368   },
1369   {
1370     EL_SP_BUGGY_BASE,
1371     EL_SP_BUGGY_BASE_ACTIVATING,
1372     0,
1373     InitBuggyBase,
1374     NULL,
1375     NULL
1376   },
1377   {
1378     EL_SP_BUGGY_BASE_ACTIVATING,
1379     EL_SP_BUGGY_BASE_ACTIVE,
1380     0,
1381     InitBuggyBase,
1382     NULL,
1383     NULL
1384   },
1385   {
1386     EL_SP_BUGGY_BASE_ACTIVE,
1387     EL_SP_BUGGY_BASE,
1388     0,
1389     InitBuggyBase,
1390     WarnBuggyBase,
1391     NULL
1392   },
1393   {
1394     EL_TRAP,
1395     EL_TRAP_ACTIVE,
1396     0,
1397     InitTrap,
1398     NULL,
1399     ActivateTrap
1400   },
1401   {
1402     EL_TRAP_ACTIVE,
1403     EL_TRAP,
1404     31,
1405     NULL,
1406     ChangeActiveTrap,
1407     NULL
1408   },
1409   {
1410     EL_ROBOT_WHEEL_ACTIVE,
1411     EL_ROBOT_WHEEL,
1412     0,
1413     InitRobotWheel,
1414     RunRobotWheel,
1415     StopRobotWheel
1416   },
1417   {
1418     EL_TIMEGATE_SWITCH_ACTIVE,
1419     EL_TIMEGATE_SWITCH,
1420     0,
1421     InitTimegateWheel,
1422     RunTimegateWheel,
1423     NULL
1424   },
1425   {
1426     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1427     EL_DC_TIMEGATE_SWITCH,
1428     0,
1429     InitTimegateWheel,
1430     RunTimegateWheel,
1431     NULL
1432   },
1433   {
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     EL_EMC_MAGIC_BALL_ACTIVE,
1436     0,
1437     InitMagicBallDelay,
1438     NULL,
1439     ActivateMagicBall
1440   },
1441   {
1442     EL_EMC_SPRING_BUMPER_ACTIVE,
1443     EL_EMC_SPRING_BUMPER,
1444     8,
1445     NULL,
1446     NULL,
1447     NULL
1448   },
1449   {
1450     EL_DIAGONAL_SHRINKING,
1451     EL_UNDEFINED,
1452     0,
1453     NULL,
1454     NULL,
1455     NULL
1456   },
1457   {
1458     EL_DIAGONAL_GROWING,
1459     EL_UNDEFINED,
1460     0,
1461     NULL,
1462     NULL,
1463     NULL,
1464   },
1465 
1466   {
1467     EL_UNDEFINED,
1468     EL_UNDEFINED,
1469     -1,
1470     NULL,
1471     NULL,
1472     NULL
1473   }
1474 };
1475 
1476 struct
1477 {
1478   int element;
1479   int push_delay_fixed, push_delay_random;
1480 }
1481 push_delay_list[] =
1482 {
1483   { EL_SPRING,			0, 0 },
1484   { EL_BALLOON,			0, 0 },
1485 
1486   { EL_SOKOBAN_OBJECT,		2, 0 },
1487   { EL_SOKOBAN_FIELD_FULL,	2, 0 },
1488   { EL_SATELLITE,		2, 0 },
1489   { EL_SP_DISK_YELLOW,		2, 0 },
1490 
1491   { EL_UNDEFINED,		0, 0 },
1492 };
1493 
1494 struct
1495 {
1496   int element;
1497   int move_stepsize;
1498 }
1499 move_stepsize_list[] =
1500 {
1501   { EL_AMOEBA_DROP,		2 },
1502   { EL_AMOEBA_DROPPING,		2 },
1503   { EL_QUICKSAND_FILLING,	1 },
1504   { EL_QUICKSAND_EMPTYING,	1 },
1505   { EL_QUICKSAND_FAST_FILLING,	2 },
1506   { EL_QUICKSAND_FAST_EMPTYING,	2 },
1507   { EL_MAGIC_WALL_FILLING,	2 },
1508   { EL_MAGIC_WALL_EMPTYING,	2 },
1509   { EL_BD_MAGIC_WALL_FILLING,	2 },
1510   { EL_BD_MAGIC_WALL_EMPTYING,	2 },
1511   { EL_DC_MAGIC_WALL_FILLING,	2 },
1512   { EL_DC_MAGIC_WALL_EMPTYING,	2 },
1513 
1514   { EL_UNDEFINED,		0 },
1515 };
1516 
1517 struct
1518 {
1519   int element;
1520   int count;
1521 }
1522 collect_count_list[] =
1523 {
1524   { EL_EMERALD,			1 },
1525   { EL_BD_DIAMOND,		1 },
1526   { EL_EMERALD_YELLOW,		1 },
1527   { EL_EMERALD_RED,		1 },
1528   { EL_EMERALD_PURPLE,		1 },
1529   { EL_DIAMOND,			3 },
1530   { EL_SP_INFOTRON,		1 },
1531   { EL_PEARL,			5 },
1532   { EL_CRYSTAL,			8 },
1533 
1534   { EL_UNDEFINED,		0 },
1535 };
1536 
1537 struct
1538 {
1539   int element;
1540   int direction;
1541 }
1542 access_direction_list[] =
1543 {
1544   { EL_TUBE_ANY,			MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1545   { EL_TUBE_VERTICAL,			                     MV_UP | MV_DOWN },
1546   { EL_TUBE_HORIZONTAL,			MV_LEFT | MV_RIGHT                   },
1547   { EL_TUBE_VERTICAL_LEFT,		MV_LEFT |            MV_UP | MV_DOWN },
1548   { EL_TUBE_VERTICAL_RIGHT,		          MV_RIGHT | MV_UP | MV_DOWN },
1549   { EL_TUBE_HORIZONTAL_UP,		MV_LEFT | MV_RIGHT | MV_UP           },
1550   { EL_TUBE_HORIZONTAL_DOWN,		MV_LEFT | MV_RIGHT |         MV_DOWN },
1551   { EL_TUBE_LEFT_UP,			MV_LEFT |            MV_UP           },
1552   { EL_TUBE_LEFT_DOWN,			MV_LEFT |                    MV_DOWN },
1553   { EL_TUBE_RIGHT_UP,			          MV_RIGHT | MV_UP           },
1554   { EL_TUBE_RIGHT_DOWN,			          MV_RIGHT |         MV_DOWN },
1555 
1556   { EL_SP_PORT_LEFT,			          MV_RIGHT                   },
1557   { EL_SP_PORT_RIGHT,			MV_LEFT                              },
1558   { EL_SP_PORT_UP,			                             MV_DOWN },
1559   { EL_SP_PORT_DOWN,			                     MV_UP           },
1560   { EL_SP_PORT_HORIZONTAL,		MV_LEFT | MV_RIGHT                   },
1561   { EL_SP_PORT_VERTICAL,		                     MV_UP | MV_DOWN },
1562   { EL_SP_PORT_ANY,			MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1563   { EL_SP_GRAVITY_PORT_LEFT,		          MV_RIGHT                   },
1564   { EL_SP_GRAVITY_PORT_RIGHT,		MV_LEFT                              },
1565   { EL_SP_GRAVITY_PORT_UP,		                             MV_DOWN },
1566   { EL_SP_GRAVITY_PORT_DOWN,		                     MV_UP           },
1567   { EL_SP_GRAVITY_ON_PORT_LEFT,		          MV_RIGHT                   },
1568   { EL_SP_GRAVITY_ON_PORT_RIGHT,	MV_LEFT                              },
1569   { EL_SP_GRAVITY_ON_PORT_UP,		                             MV_DOWN },
1570   { EL_SP_GRAVITY_ON_PORT_DOWN,		                     MV_UP           },
1571   { EL_SP_GRAVITY_OFF_PORT_LEFT,	          MV_RIGHT                   },
1572   { EL_SP_GRAVITY_OFF_PORT_RIGHT,	MV_LEFT                              },
1573   { EL_SP_GRAVITY_OFF_PORT_UP,		                             MV_DOWN },
1574   { EL_SP_GRAVITY_OFF_PORT_DOWN,	                     MV_UP           },
1575 
1576   { EL_UNDEFINED,			MV_NONE				     }
1577 };
1578 
1579 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1580 
1581 #define IS_AUTO_CHANGING(e)	(element_info[e].has_change_event[CE_DELAY])
1582 #define IS_JUST_CHANGING(x, y)	(ChangeDelay[x][y] != 0)
1583 #define IS_CHANGING(x, y)	(IS_AUTO_CHANGING(Feld[x][y]) || \
1584 				 IS_JUST_CHANGING(x, y))
1585 
1586 #define CE_PAGE(e, ce)		(element_info[e].event_page[ce])
1587 
1588 /* static variables for playfield scan mode (scanning forward or backward) */
1589 static int playfield_scan_start_x = 0;
1590 static int playfield_scan_start_y = 0;
1591 static int playfield_scan_delta_x = 1;
1592 static int playfield_scan_delta_y = 1;
1593 
1594 #define SCAN_PLAYFIELD(x, y)	for ((y) = playfield_scan_start_y;	\
1595 				     (y) >= 0 && (y) <= lev_fieldy - 1;	\
1596 				     (y) += playfield_scan_delta_y)	\
1597 				for ((x) = playfield_scan_start_x;	\
1598 				     (x) >= 0 && (x) <= lev_fieldx - 1;	\
1599 				     (x) += playfield_scan_delta_x)
1600 
1601 #ifdef DEBUG
DEBUG_SetMaximumDynamite()1602 void DEBUG_SetMaximumDynamite()
1603 {
1604   int i;
1605 
1606   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1607     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1608       local_player->inventory_element[local_player->inventory_size++] =
1609 	EL_DYNAMITE;
1610 }
1611 #endif
1612 
InitPlayfieldScanModeVars()1613 static void InitPlayfieldScanModeVars()
1614 {
1615   if (game.use_reverse_scan_direction)
1616   {
1617     playfield_scan_start_x = lev_fieldx - 1;
1618     playfield_scan_start_y = lev_fieldy - 1;
1619 
1620     playfield_scan_delta_x = -1;
1621     playfield_scan_delta_y = -1;
1622   }
1623   else
1624   {
1625     playfield_scan_start_x = 0;
1626     playfield_scan_start_y = 0;
1627 
1628     playfield_scan_delta_x = 1;
1629     playfield_scan_delta_y = 1;
1630   }
1631 }
1632 
InitPlayfieldScanMode(int mode)1633 static void InitPlayfieldScanMode(int mode)
1634 {
1635   game.use_reverse_scan_direction =
1636     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1637 
1638   InitPlayfieldScanModeVars();
1639 }
1640 
get_move_delay_from_stepsize(int move_stepsize)1641 static int get_move_delay_from_stepsize(int move_stepsize)
1642 {
1643   move_stepsize =
1644     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1645 
1646   /* make sure that stepsize value is always a power of 2 */
1647   move_stepsize = (1 << log_2(move_stepsize));
1648 
1649   return TILEX / move_stepsize;
1650 }
1651 
SetPlayerMoveSpeed(struct PlayerInfo * player,int move_stepsize,boolean init_game)1652 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1653 			       boolean init_game)
1654 {
1655   int player_nr = player->index_nr;
1656   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1657   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1658 
1659   /* do no immediately change move delay -- the player might just be moving */
1660   player->move_delay_value_next = move_delay;
1661 
1662   /* information if player can move must be set separately */
1663   player->cannot_move = cannot_move;
1664 
1665   if (init_game)
1666   {
1667     player->move_delay       = game.initial_move_delay[player_nr];
1668     player->move_delay_value = game.initial_move_delay_value[player_nr];
1669 
1670     player->move_delay_value_next = -1;
1671 
1672     player->move_delay_reset_counter = 0;
1673   }
1674 }
1675 
GetPlayerConfig()1676 void GetPlayerConfig()
1677 {
1678   GameFrameDelay = setup.game_frame_delay;
1679 
1680   if (!audio.sound_available)
1681     setup.sound_simple = FALSE;
1682 
1683   if (!audio.loops_available)
1684     setup.sound_loops = FALSE;
1685 
1686   if (!audio.music_available)
1687     setup.sound_music = FALSE;
1688 
1689   if (!video.fullscreen_available)
1690     setup.fullscreen = FALSE;
1691 
1692   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1693 
1694   SetAudioMode(setup.sound);
1695   InitJoysticks();
1696 }
1697 
GetElementFromGroupElement(int element)1698 int GetElementFromGroupElement(int element)
1699 {
1700   if (IS_GROUP_ELEMENT(element))
1701   {
1702     struct ElementGroupInfo *group = element_info[element].group;
1703     int last_anim_random_frame = gfx.anim_random_frame;
1704     int element_pos;
1705 
1706     if (group->choice_mode == ANIM_RANDOM)
1707       gfx.anim_random_frame = RND(group->num_elements_resolved);
1708 
1709     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1710 				    group->choice_mode, 0,
1711 				    group->choice_pos);
1712 
1713     if (group->choice_mode == ANIM_RANDOM)
1714       gfx.anim_random_frame = last_anim_random_frame;
1715 
1716     group->choice_pos++;
1717 
1718     element = group->element_resolved[element_pos];
1719   }
1720 
1721   return element;
1722 }
1723 
InitPlayerField(int x,int y,int element,boolean init_game)1724 static void InitPlayerField(int x, int y, int element, boolean init_game)
1725 {
1726   if (element == EL_SP_MURPHY)
1727   {
1728     if (init_game)
1729     {
1730       if (stored_player[0].present)
1731       {
1732 	Feld[x][y] = EL_SP_MURPHY_CLONE;
1733 
1734 	return;
1735       }
1736       else
1737       {
1738 	stored_player[0].initial_element = element;
1739 	stored_player[0].use_murphy = TRUE;
1740 
1741 	if (!level.use_artwork_element[0])
1742 	  stored_player[0].artwork_element = EL_SP_MURPHY;
1743       }
1744 
1745       Feld[x][y] = EL_PLAYER_1;
1746     }
1747   }
1748 
1749   if (init_game)
1750   {
1751     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1752     int jx = player->jx, jy = player->jy;
1753 
1754     player->present = TRUE;
1755 
1756     player->block_last_field = (element == EL_SP_MURPHY ?
1757 				level.sp_block_last_field :
1758 				level.block_last_field);
1759 
1760     /* ---------- initialize player's last field block delay --------------- */
1761 
1762     /* always start with reliable default value (no adjustment needed) */
1763     player->block_delay_adjustment = 0;
1764 
1765     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1766     if (player->block_last_field && element == EL_SP_MURPHY)
1767       player->block_delay_adjustment = 1;
1768 
1769     /* special case 2: in game engines before 3.1.1, blocking was different */
1770     if (game.use_block_last_field_bug)
1771       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1772 
1773     if (!options.network || player->connected)
1774     {
1775       player->active = TRUE;
1776 
1777       /* remove potentially duplicate players */
1778       if (StorePlayer[jx][jy] == Feld[x][y])
1779 	StorePlayer[jx][jy] = 0;
1780 
1781       StorePlayer[x][y] = Feld[x][y];
1782 
1783       if (options.debug)
1784       {
1785 	printf("Player %d activated.\n", player->element_nr);
1786 	printf("[Local player is %d and currently %s.]\n",
1787 	       local_player->element_nr,
1788 	       local_player->active ? "active" : "not active");
1789       }
1790     }
1791 
1792     Feld[x][y] = EL_EMPTY;
1793 
1794     player->jx = player->last_jx = x;
1795     player->jy = player->last_jy = y;
1796   }
1797 
1798 #if USE_PLAYER_REANIMATION
1799   if (!init_game)
1800   {
1801     int player_nr = GET_PLAYER_NR(element);
1802     struct PlayerInfo *player = &stored_player[player_nr];
1803 
1804     if (player->active && player->killed)
1805       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1806   }
1807 #endif
1808 }
1809 
InitField(int x,int y,boolean init_game)1810 static void InitField(int x, int y, boolean init_game)
1811 {
1812   int element = Feld[x][y];
1813 
1814   switch (element)
1815   {
1816     case EL_SP_MURPHY:
1817     case EL_PLAYER_1:
1818     case EL_PLAYER_2:
1819     case EL_PLAYER_3:
1820     case EL_PLAYER_4:
1821       InitPlayerField(x, y, element, init_game);
1822       break;
1823 
1824     case EL_SOKOBAN_FIELD_PLAYER:
1825       element = Feld[x][y] = EL_PLAYER_1;
1826       InitField(x, y, init_game);
1827 
1828       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1829       InitField(x, y, init_game);
1830       break;
1831 
1832     case EL_SOKOBAN_FIELD_EMPTY:
1833       local_player->sokobanfields_still_needed++;
1834       break;
1835 
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1838 	Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1840 	Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842 	Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1844 	Feld[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846 	Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848 
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888       InitMovDir(x, y);
1889       break;
1890 
1891     case EL_AMOEBA_FULL:
1892     case EL_BD_AMOEBA:
1893       InitAmoebaNr(x, y);
1894       break;
1895 
1896     case EL_AMOEBA_DROP:
1897       if (y == lev_fieldy - 1)
1898       {
1899 	Feld[x][y] = EL_AMOEBA_GROWING;
1900 	Store[x][y] = EL_AMOEBA_WET;
1901       }
1902       break;
1903 
1904     case EL_DYNAMITE_ACTIVE:
1905     case EL_SP_DISK_RED_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1910       MovDelay[x][y] = 96;
1911       break;
1912 
1913     case EL_EM_DYNAMITE_ACTIVE:
1914       MovDelay[x][y] = 32;
1915       break;
1916 
1917     case EL_LAMP:
1918       local_player->lights_still_needed++;
1919       break;
1920 
1921     case EL_PENGUIN:
1922       local_player->friends_still_needed++;
1923       break;
1924 
1925     case EL_PIG:
1926     case EL_DRAGON:
1927       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1928       break;
1929 
1930     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1931     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1932     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1933     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1934     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1935     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1936     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1937     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1938     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1939     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1940     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1941     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1942       if (init_game)
1943       {
1944 	int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1945 	int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1946 	int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1947 
1948 	if (game.belt_dir_nr[belt_nr] == 3)	/* initial value */
1949 	{
1950 	  game.belt_dir[belt_nr] = belt_dir;
1951 	  game.belt_dir_nr[belt_nr] = belt_dir_nr;
1952 	}
1953 	else	/* more than one switch -- set it like the first switch */
1954 	{
1955 	  Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1956 	}
1957       }
1958       break;
1959 
1960 #if !USE_BOTH_SWITCHGATE_SWITCHES
1961     case EL_SWITCHGATE_SWITCH_DOWN:	/* always start with same switch pos */
1962       if (init_game)
1963 	Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1964       break;
1965 
1966     case EL_DC_SWITCHGATE_SWITCH_DOWN:	/* always start with same switch pos */
1967       if (init_game)
1968 	Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1969       break;
1970 #endif
1971 
1972     case EL_LIGHT_SWITCH_ACTIVE:
1973       if (init_game)
1974 	game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1975       break;
1976 
1977     case EL_INVISIBLE_STEELWALL:
1978     case EL_INVISIBLE_WALL:
1979     case EL_INVISIBLE_SAND:
1980       if (game.light_time_left > 0 ||
1981 	  game.lenses_time_left > 0)
1982         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1983       break;
1984 
1985     case EL_EMC_MAGIC_BALL:
1986       if (game.ball_state)
1987 	Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1988       break;
1989 
1990     case EL_EMC_MAGIC_BALL_SWITCH:
1991       if (game.ball_state)
1992 	Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1993       break;
1994 
1995     case EL_TRIGGER_PLAYER:
1996     case EL_TRIGGER_ELEMENT:
1997     case EL_TRIGGER_CE_VALUE:
1998     case EL_TRIGGER_CE_SCORE:
1999     case EL_SELF:
2000     case EL_ANY_ELEMENT:
2001     case EL_CURRENT_CE_VALUE:
2002     case EL_CURRENT_CE_SCORE:
2003     case EL_PREV_CE_1:
2004     case EL_PREV_CE_2:
2005     case EL_PREV_CE_3:
2006     case EL_PREV_CE_4:
2007     case EL_PREV_CE_5:
2008     case EL_PREV_CE_6:
2009     case EL_PREV_CE_7:
2010     case EL_PREV_CE_8:
2011     case EL_NEXT_CE_1:
2012     case EL_NEXT_CE_2:
2013     case EL_NEXT_CE_3:
2014     case EL_NEXT_CE_4:
2015     case EL_NEXT_CE_5:
2016     case EL_NEXT_CE_6:
2017     case EL_NEXT_CE_7:
2018     case EL_NEXT_CE_8:
2019       /* reference elements should not be used on the playfield */
2020       Feld[x][y] = EL_EMPTY;
2021       break;
2022 
2023     default:
2024       if (IS_CUSTOM_ELEMENT(element))
2025       {
2026 	if (CAN_MOVE(element))
2027 	  InitMovDir(x, y);
2028 
2029 #if USE_NEW_CUSTOM_VALUE
2030 	if (!element_info[element].use_last_ce_value || init_game)
2031 	  CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2032 #endif
2033       }
2034       else if (IS_GROUP_ELEMENT(element))
2035       {
2036 	Feld[x][y] = GetElementFromGroupElement(element);
2037 
2038 	InitField(x, y, init_game);
2039       }
2040 
2041       break;
2042   }
2043 
2044   if (!init_game)
2045     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2046 }
2047 
InitField_WithBug1(int x,int y,boolean init_game)2048 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2049 {
2050   InitField(x, y, init_game);
2051 
2052   /* not needed to call InitMovDir() -- already done by InitField()! */
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(Feld[x][y]))
2055     InitMovDir(x, y);
2056 }
2057 
InitField_WithBug2(int x,int y,boolean init_game)2058 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2059 {
2060   int old_element = Feld[x][y];
2061 
2062   InitField(x, y, init_game);
2063 
2064   /* not needed to call InitMovDir() -- already done by InitField()! */
2065   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2066       CAN_MOVE(old_element) &&
2067       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2068     InitMovDir(x, y);
2069 
2070   /* this case is in fact a combination of not less than three bugs:
2071      first, it calls InitMovDir() for elements that can move, although this is
2072      already done by InitField(); then, it checks the element that was at this
2073      field _before_ the call to InitField() (which can change it); lastly, it
2074      was not called for "mole with direction" elements, which were treated as
2075      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2076   */
2077 }
2078 
2079 #if 1
2080 
get_key_element_from_nr(int key_nr)2081 static int get_key_element_from_nr(int key_nr)
2082 {
2083   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2084 			  level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2085 			  EL_EM_KEY_1 : EL_KEY_1);
2086 
2087   return key_base_element + key_nr;
2088 }
2089 
get_next_dropped_element(struct PlayerInfo * player)2090 static int get_next_dropped_element(struct PlayerInfo *player)
2091 {
2092   return (player->inventory_size > 0 ?
2093 	  player->inventory_element[player->inventory_size - 1] :
2094 	  player->inventory_infinite_element != EL_UNDEFINED ?
2095 	  player->inventory_infinite_element :
2096 	  player->dynabombs_left > 0 ?
2097 	  EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2098 	  EL_UNDEFINED);
2099 }
2100 
get_inventory_element_from_pos(struct PlayerInfo * player,int pos)2101 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2102 {
2103   /* pos >= 0: get element from bottom of the stack;
2104      pos <  0: get element from top of the stack */
2105 
2106   if (pos < 0)
2107   {
2108     int min_inventory_size = -pos;
2109     int inventory_pos = player->inventory_size - min_inventory_size;
2110     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2111 
2112     return (player->inventory_size >= min_inventory_size ?
2113 	    player->inventory_element[inventory_pos] :
2114 	    player->inventory_infinite_element != EL_UNDEFINED ?
2115 	    player->inventory_infinite_element :
2116 	    player->dynabombs_left >= min_dynabombs_left ?
2117 	    EL_DYNABOMB_PLAYER_1 + player->index_nr :
2118 	    EL_UNDEFINED);
2119   }
2120   else
2121   {
2122     int min_dynabombs_left = pos + 1;
2123     int min_inventory_size = pos + 1 - player->dynabombs_left;
2124     int inventory_pos = pos - player->dynabombs_left;
2125 
2126     return (player->inventory_infinite_element != EL_UNDEFINED ?
2127 	    player->inventory_infinite_element :
2128 	    player->dynabombs_left >= min_dynabombs_left ?
2129 	    EL_DYNABOMB_PLAYER_1 + player->index_nr :
2130 	    player->inventory_size >= min_inventory_size ?
2131 	    player->inventory_element[inventory_pos] :
2132 	    EL_UNDEFINED);
2133   }
2134 }
2135 
compareGamePanelOrderInfo(const void * object1,const void * object2)2136 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2137 {
2138   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2139   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2140   int compare_result;
2141 
2142   if (gpo1->sort_priority != gpo2->sort_priority)
2143     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2144   else
2145     compare_result = gpo1->nr - gpo2->nr;
2146 
2147   return compare_result;
2148 }
2149 
InitGameControlValues()2150 void InitGameControlValues()
2151 {
2152   int i;
2153 
2154   for (i = 0; game_panel_controls[i].nr != -1; i++)
2155   {
2156     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2157     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2158     struct TextPosInfo *pos = gpc->pos;
2159     int nr = gpc->nr;
2160     int type = gpc->type;
2161 
2162     if (nr != i)
2163     {
2164       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2165       Error(ERR_EXIT, "this should not happen -- please debug");
2166     }
2167 
2168     /* force update of game controls after initialization */
2169     gpc->value = gpc->last_value = -1;
2170     gpc->frame = gpc->last_frame = -1;
2171     gpc->gfx_frame = -1;
2172 
2173     /* determine panel value width for later calculation of alignment */
2174     if (type == TYPE_INTEGER || type == TYPE_STRING)
2175     {
2176       pos->width = pos->size * getFontWidth(pos->font);
2177       pos->height = getFontHeight(pos->font);
2178     }
2179     else if (type == TYPE_ELEMENT)
2180     {
2181       pos->width = pos->size;
2182       pos->height = pos->size;
2183     }
2184 
2185     /* fill structure for game panel draw order */
2186     gpo->nr = gpc->nr;
2187     gpo->sort_priority = pos->sort_priority;
2188   }
2189 
2190   /* sort game panel controls according to sort_priority and control number */
2191   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2192 	sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2193 }
2194 
UpdatePlayfieldElementCount()2195 void UpdatePlayfieldElementCount()
2196 {
2197   boolean use_element_count = FALSE;
2198   int i, j, x, y;
2199 
2200   /* first check if it is needed at all to calculate playfield element count */
2201   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2202     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2203       use_element_count = TRUE;
2204 
2205   if (!use_element_count)
2206     return;
2207 
2208   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2209     element_info[i].element_count = 0;
2210 
2211   SCAN_PLAYFIELD(x, y)
2212   {
2213     element_info[Feld[x][y]].element_count++;
2214   }
2215 
2216   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2217     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2218       if (IS_IN_GROUP(j, i))
2219 	element_info[EL_GROUP_START + i].element_count +=
2220 	  element_info[j].element_count;
2221 }
2222 
UpdateGameControlValues()2223 void UpdateGameControlValues()
2224 {
2225   int i, k;
2226   int time = (local_player->LevelSolved ?
2227 	      local_player->LevelSolved_CountingTime :
2228 	      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2229 	      level.native_em_level->lev->time :
2230 	      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2231 	      level.native_sp_level->game_sp->time_played :
2232 	      level.time == 0 ? TimePlayed : TimeLeft);
2233   int score = (local_player->LevelSolved ?
2234 	       local_player->LevelSolved_CountingScore :
2235 	       level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2236 	       level.native_em_level->lev->score :
2237 	       level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2238 	       level.native_sp_level->game_sp->score :
2239 	       local_player->score);
2240   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2241 	      level.native_em_level->lev->required :
2242 	      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2243 	      level.native_sp_level->game_sp->infotrons_still_needed :
2244 	      local_player->gems_still_needed);
2245   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2246 		     level.native_em_level->lev->required > 0 :
2247 		     level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2248 		     level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2249 		     local_player->gems_still_needed > 0 ||
2250 		     local_player->sokobanfields_still_needed > 0 ||
2251 		     local_player->lights_still_needed > 0);
2252 
2253   UpdatePlayfieldElementCount();
2254 
2255   /* update game panel control values */
2256 
2257   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2258   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2259 
2260   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2261   for (i = 0; i < MAX_NUM_KEYS; i++)
2262     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2263   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2264   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2265 
2266   if (game.centered_player_nr == -1)
2267   {
2268     for (i = 0; i < MAX_PLAYERS; i++)
2269     {
2270       /* only one player in Supaplex game engine */
2271       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2272 	break;
2273 
2274       for (k = 0; k < MAX_NUM_KEYS; k++)
2275       {
2276 	if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2277 	{
2278 	  if (level.native_em_level->ply[i]->keys & (1 << k))
2279 	    game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2280 	      get_key_element_from_nr(k);
2281 	}
2282 	else if (stored_player[i].key[k])
2283 	  game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2284 	    get_key_element_from_nr(k);
2285       }
2286 
2287       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2288 	game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2289 	  level.native_em_level->ply[i]->dynamite;
2290       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2291 	game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2292 	  level.native_sp_level->game_sp->red_disk_count;
2293       else
2294 	game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2295 	  stored_player[i].inventory_size;
2296 
2297       if (stored_player[i].num_white_keys > 0)
2298 	game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2299 	  EL_DC_KEY_WHITE;
2300 
2301       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2302 	stored_player[i].num_white_keys;
2303     }
2304   }
2305   else
2306   {
2307     int player_nr = game.centered_player_nr;
2308 
2309     for (k = 0; k < MAX_NUM_KEYS; k++)
2310     {
2311       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2312       {
2313 	if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2314 	  game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2315 	    get_key_element_from_nr(k);
2316       }
2317       else if (stored_player[player_nr].key[k])
2318 	game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2319 	  get_key_element_from_nr(k);
2320     }
2321 
2322     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2323       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2324 	level.native_em_level->ply[player_nr]->dynamite;
2325     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2326       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2327 	level.native_sp_level->game_sp->red_disk_count;
2328     else
2329       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330 	stored_player[player_nr].inventory_size;
2331 
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334 
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338 
2339   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2340   {
2341     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2342       get_inventory_element_from_pos(local_player, i);
2343     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2344       get_inventory_element_from_pos(local_player, -i - 1);
2345   }
2346 
2347   game_panel_controls[GAME_PANEL_SCORE].value = score;
2348   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2349 
2350   game_panel_controls[GAME_PANEL_TIME].value = time;
2351 
2352   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2353   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2354   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2355 
2356   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2357 
2358   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2359     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2360      EL_EMPTY);
2361   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2362     local_player->shield_normal_time_left;
2363   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2364     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2365      EL_EMPTY);
2366   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2367     local_player->shield_deadly_time_left;
2368 
2369   game_panel_controls[GAME_PANEL_EXIT].value =
2370     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2371 
2372   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2373     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2374   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2375     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2376      EL_EMC_MAGIC_BALL_SWITCH);
2377 
2378   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2379     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2380   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2381     game.light_time_left;
2382 
2383   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2384     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2385   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2386     game.timegate_time_left;
2387 
2388   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2389     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2390 
2391   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2392     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2393   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2394     game.lenses_time_left;
2395 
2396   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2397     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2398   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2399     game.magnify_time_left;
2400 
2401   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2402     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2403      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2404      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2405      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2406      EL_BALLOON_SWITCH_NONE);
2407 
2408   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2409     local_player->dynabomb_count;
2410   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2411     local_player->dynabomb_size;
2412   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2413     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2414 
2415   game_panel_controls[GAME_PANEL_PENGUINS].value =
2416     local_player->friends_still_needed;
2417 
2418   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2419     local_player->sokobanfields_still_needed;
2420   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2421     local_player->sokobanfields_still_needed;
2422 
2423   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2424     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2425 
2426   for (i = 0; i < NUM_BELTS; i++)
2427   {
2428     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2429       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2430        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2431     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2432       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2433   }
2434 
2435   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2436     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2437   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2438     game.magic_wall_time_left;
2439 
2440 #if USE_PLAYER_GRAVITY
2441   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2442     local_player->gravity;
2443 #else
2444   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2445 #endif
2446 
2447   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2448     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2449 
2450   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2451     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2452       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2453        game.panel.element[i].id : EL_UNDEFINED);
2454 
2455   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2456     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2457       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2458        element_info[game.panel.element_count[i].id].element_count : 0);
2459 
2460   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2461     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2462       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2463        element_info[game.panel.ce_score[i].id].collect_score : 0);
2464 
2465   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2466     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2467       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2468        element_info[game.panel.ce_score_element[i].id].collect_score :
2469        EL_UNDEFINED);
2470 
2471   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2472   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2473   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2474 
2475   /* update game panel control frames */
2476 
2477   for (i = 0; game_panel_controls[i].nr != -1; i++)
2478   {
2479     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2480 
2481     if (gpc->type == TYPE_ELEMENT)
2482     {
2483       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2484       {
2485 	int last_anim_random_frame = gfx.anim_random_frame;
2486 	int element = gpc->value;
2487 	int graphic = el2panelimg(element);
2488 
2489 	if (gpc->value != gpc->last_value)
2490 	{
2491 	  gpc->gfx_frame = 0;
2492 	  gpc->gfx_random = INIT_GFX_RANDOM();
2493 	}
2494 	else
2495 	{
2496 	  gpc->gfx_frame++;
2497 
2498 	  if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2499 	      IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2500 	    gpc->gfx_random = INIT_GFX_RANDOM();
2501 	}
2502 
2503 	if (ANIM_MODE(graphic) == ANIM_RANDOM)
2504 	  gfx.anim_random_frame = gpc->gfx_random;
2505 
2506 	if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2507 	  gpc->gfx_frame = element_info[element].collect_score;
2508 
2509 	gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2510 					      gpc->gfx_frame);
2511 
2512 	if (ANIM_MODE(graphic) == ANIM_RANDOM)
2513 	  gfx.anim_random_frame = last_anim_random_frame;
2514       }
2515     }
2516   }
2517 }
2518 
DisplayGameControlValues()2519 void DisplayGameControlValues()
2520 {
2521   boolean redraw_panel = FALSE;
2522   int i;
2523 
2524   for (i = 0; game_panel_controls[i].nr != -1; i++)
2525   {
2526     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2527 
2528     if (PANEL_DEACTIVATED(gpc->pos))
2529       continue;
2530 
2531     if (gpc->value == gpc->last_value &&
2532 	gpc->frame == gpc->last_frame)
2533       continue;
2534 
2535     redraw_panel = TRUE;
2536   }
2537 
2538   if (!redraw_panel)
2539     return;
2540 
2541   /* copy default game door content to main double buffer */
2542   BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2543 	     DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2544 
2545   /* redraw game control buttons */
2546 #if 1
2547   RedrawGameButtons();
2548 #else
2549   UnmapGameButtons();
2550   MapGameButtons();
2551 #endif
2552 
2553   game_status = GAME_MODE_PSEUDO_PANEL;
2554 
2555 #if 1
2556   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2557 #else
2558   for (i = 0; game_panel_controls[i].nr != -1; i++)
2559 #endif
2560   {
2561 #if 1
2562     int nr = game_panel_order[i].nr;
2563     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2564 #else
2565     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2566     int nr = gpc->nr;
2567 #endif
2568     struct TextPosInfo *pos = gpc->pos;
2569     int type = gpc->type;
2570     int value = gpc->value;
2571     int frame = gpc->frame;
2572 #if 0
2573     int last_value = gpc->last_value;
2574     int last_frame = gpc->last_frame;
2575 #endif
2576     int size = pos->size;
2577     int font = pos->font;
2578     boolean draw_masked = pos->draw_masked;
2579     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2580 
2581     if (PANEL_DEACTIVATED(pos))
2582       continue;
2583 
2584 #if 0
2585     if (value == last_value && frame == last_frame)
2586       continue;
2587 #endif
2588 
2589     gpc->last_value = value;
2590     gpc->last_frame = frame;
2591 
2592 #if 0
2593     printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2594 #endif
2595 
2596     if (type == TYPE_INTEGER)
2597     {
2598       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2599 	  nr == GAME_PANEL_TIME)
2600       {
2601 	boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2602 
2603 	if (use_dynamic_size)		/* use dynamic number of digits */
2604 	{
2605 	  int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2606 	  int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2607 	  int size2 = size1 + 1;
2608 	  int font1 = pos->font;
2609 	  int font2 = pos->font_alt;
2610 
2611 	  size = (value < value_change ? size1 : size2);
2612 	  font = (value < value_change ? font1 : font2);
2613 
2614 #if 0
2615 	  /* clear background if value just changed its size (dynamic digits) */
2616 	  if ((last_value < value_change) != (value < value_change))
2617 	  {
2618 	    int width1 = size1 * getFontWidth(font1);
2619 	    int width2 = size2 * getFontWidth(font2);
2620 	    int max_width = MAX(width1, width2);
2621 	    int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2622 
2623 	    pos->width = max_width;
2624 
2625 	    ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2626 				       max_width, max_height);
2627 	  }
2628 #endif
2629 	}
2630       }
2631 
2632 #if 1
2633       /* correct text size if "digits" is zero or less */
2634       if (size <= 0)
2635 	size = strlen(int2str(value, size));
2636 
2637       /* dynamically correct text alignment */
2638       pos->width = size * getFontWidth(font);
2639 #endif
2640 
2641       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2642 		  int2str(value, size), font, mask_mode);
2643     }
2644     else if (type == TYPE_ELEMENT)
2645     {
2646       int element, graphic;
2647       Bitmap *src_bitmap;
2648       int src_x, src_y;
2649       int width, height;
2650       int dst_x = PANEL_XPOS(pos);
2651       int dst_y = PANEL_YPOS(pos);
2652 
2653 #if 1
2654       if (value != EL_UNDEFINED && value != EL_EMPTY)
2655       {
2656 	element = value;
2657 	graphic = el2panelimg(value);
2658 
2659 	// printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2660 
2661 #if 1
2662 	if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2663 	  size = TILESIZE;
2664 #endif
2665 
2666 	getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2667 			      &src_x, &src_y);
2668 
2669 	width  = graphic_info[graphic].width  * size / TILESIZE;
2670 	height = graphic_info[graphic].height * size / TILESIZE;
2671 
2672 	if (draw_masked)
2673 	{
2674 	  SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2675 			dst_x - src_x, dst_y - src_y);
2676 	  BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2677 			   dst_x, dst_y);
2678 	}
2679 	else
2680 	{
2681 	  BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2682 		     dst_x, dst_y);
2683 	}
2684       }
2685 #else
2686       if (value == EL_UNDEFINED || value == EL_EMPTY)
2687       {
2688 	element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2689 	graphic = el2panelimg(element);
2690 
2691 	src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2692 	src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2693 	src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2694       }
2695       else
2696       {
2697 	element = value;
2698 	graphic = el2panelimg(value);
2699 
2700 	getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2701       }
2702 
2703       width  = graphic_info[graphic].width  * size / TILESIZE;
2704       height = graphic_info[graphic].height * size / TILESIZE;
2705 
2706       BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2707 #endif
2708     }
2709     else if (type == TYPE_STRING)
2710     {
2711       boolean active = (value != 0);
2712       char *state_normal = "off";
2713       char *state_active = "on";
2714       char *state = (active ? state_active : state_normal);
2715       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2716 		 nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2717 		 nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2718 		 nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2719 
2720       if (nr == GAME_PANEL_GRAVITY_STATE)
2721       {
2722 	int font1 = pos->font;		/* (used for normal state) */
2723 	int font2 = pos->font_alt;	/* (used for active state) */
2724 #if 0
2725 	int size1 = strlen(state_normal);
2726 	int size2 = strlen(state_active);
2727 	int width1 = size1 * getFontWidth(font1);
2728 	int width2 = size2 * getFontWidth(font2);
2729 	int max_width = MAX(width1, width2);
2730 	int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2731 
2732 	pos->width = max_width;
2733 
2734 	/* clear background for values that may have changed its size */
2735 	ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2736 				   max_width, max_height);
2737 #endif
2738 
2739 	font = (active ? font2 : font1);
2740       }
2741 
2742       if (s != NULL)
2743       {
2744 	char *s_cut;
2745 
2746 #if 1
2747 	if (size <= 0)
2748 	{
2749 	  /* don't truncate output if "chars" is zero or less */
2750 	  size = strlen(s);
2751 
2752 	  /* dynamically correct text alignment */
2753 	  pos->width = size * getFontWidth(font);
2754 	}
2755 #endif
2756 
2757 	s_cut = getStringCopyN(s, size);
2758 
2759 	DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2760 		    s_cut, font, mask_mode);
2761 
2762 	free(s_cut);
2763       }
2764     }
2765 
2766     redraw_mask |= REDRAW_DOOR_1;
2767   }
2768 
2769   game_status = GAME_MODE_PLAYING;
2770 }
2771 
UpdateAndDisplayGameControlValues()2772 void UpdateAndDisplayGameControlValues()
2773 {
2774   if (tape.warp_forward)
2775     return;
2776 
2777   UpdateGameControlValues();
2778   DisplayGameControlValues();
2779 }
2780 
DrawGameValue_Emeralds(int value)2781 void DrawGameValue_Emeralds(int value)
2782 {
2783   struct TextPosInfo *pos = &game.panel.gems;
2784 #if 1
2785   int font_nr = pos->font;
2786 #else
2787   int font_nr = FONT_TEXT_2;
2788 #endif
2789   int font_width = getFontWidth(font_nr);
2790   int chars = pos->size;
2791 
2792 #if 1
2793   return;	/* !!! USE NEW STUFF !!! */
2794 #endif
2795 
2796   if (PANEL_DEACTIVATED(pos))
2797     return;
2798 
2799   pos->width = chars * font_width;
2800 
2801   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2802 }
2803 
DrawGameValue_Dynamite(int value)2804 void DrawGameValue_Dynamite(int value)
2805 {
2806   struct TextPosInfo *pos = &game.panel.inventory_count;
2807 #if 1
2808   int font_nr = pos->font;
2809 #else
2810   int font_nr = FONT_TEXT_2;
2811 #endif
2812   int font_width = getFontWidth(font_nr);
2813   int chars = pos->size;
2814 
2815 #if 1
2816   return;	/* !!! USE NEW STUFF !!! */
2817 #endif
2818 
2819   if (PANEL_DEACTIVATED(pos))
2820     return;
2821 
2822   pos->width = chars * font_width;
2823 
2824   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2825 }
2826 
DrawGameValue_Score(int value)2827 void DrawGameValue_Score(int value)
2828 {
2829   struct TextPosInfo *pos = &game.panel.score;
2830 #if 1
2831   int font_nr = pos->font;
2832 #else
2833   int font_nr = FONT_TEXT_2;
2834 #endif
2835   int font_width = getFontWidth(font_nr);
2836   int chars = pos->size;
2837 
2838 #if 1
2839   return;	/* !!! USE NEW STUFF !!! */
2840 #endif
2841 
2842   if (PANEL_DEACTIVATED(pos))
2843     return;
2844 
2845   pos->width = chars * font_width;
2846 
2847   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2848 }
2849 
DrawGameValue_Time(int value)2850 void DrawGameValue_Time(int value)
2851 {
2852   struct TextPosInfo *pos = &game.panel.time;
2853   static int last_value = -1;
2854   int chars1 = 3;
2855   int chars2 = 4;
2856   int chars = pos->size;
2857 #if 1
2858   int font1_nr = pos->font;
2859   int font2_nr = pos->font_alt;
2860 #else
2861   int font1_nr = FONT_TEXT_2;
2862   int font2_nr = FONT_TEXT_1;
2863 #endif
2864   int font_nr = font1_nr;
2865   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2866 
2867 #if 1
2868   return;	/* !!! USE NEW STUFF !!! */
2869 #endif
2870 
2871   if (PANEL_DEACTIVATED(pos))
2872     return;
2873 
2874   if (use_dynamic_chars)		/* use dynamic number of chars */
2875   {
2876     chars   = (value < 1000 ? chars1   : chars2);
2877     font_nr = (value < 1000 ? font1_nr : font2_nr);
2878   }
2879 
2880   /* clear background if value just changed its size (dynamic chars only) */
2881   if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2882   {
2883     int width1 = chars1 * getFontWidth(font1_nr);
2884     int width2 = chars2 * getFontWidth(font2_nr);
2885     int max_width = MAX(width1, width2);
2886     int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2887 
2888     pos->width = max_width;
2889 
2890     ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2891 			       max_width, max_height);
2892   }
2893 
2894   pos->width = chars * getFontWidth(font_nr);
2895 
2896   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2897 
2898   last_value = value;
2899 }
2900 
DrawGameValue_Level(int value)2901 void DrawGameValue_Level(int value)
2902 {
2903   struct TextPosInfo *pos = &game.panel.level_number;
2904   int chars1 = 2;
2905   int chars2 = 3;
2906   int chars = pos->size;
2907 #if 1
2908   int font1_nr = pos->font;
2909   int font2_nr = pos->font_alt;
2910 #else
2911   int font1_nr = FONT_TEXT_2;
2912   int font2_nr = FONT_TEXT_1;
2913 #endif
2914   int font_nr = font1_nr;
2915   boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2916 
2917 #if 1
2918   return;	/* !!! USE NEW STUFF !!! */
2919 #endif
2920 
2921   if (PANEL_DEACTIVATED(pos))
2922     return;
2923 
2924   if (use_dynamic_chars)		/* use dynamic number of chars */
2925   {
2926     chars   = (level_nr < 100 ? chars1   : chars2);
2927     font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2928   }
2929 
2930   pos->width = chars * getFontWidth(font_nr);
2931 
2932   DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2933 }
2934 
DrawGameValue_Keys(int key[MAX_NUM_KEYS])2935 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2936 {
2937 #if 0
2938   struct TextPosInfo *pos = &game.panel.keys;
2939 #endif
2940 #if 0
2941   int base_key_graphic = EL_KEY_1;
2942 #endif
2943   int i;
2944 
2945 #if 1
2946   return;	/* !!! USE NEW STUFF !!! */
2947 #endif
2948 
2949 #if 0
2950   if (PANEL_DEACTIVATED(pos))
2951     return;
2952 #endif
2953 
2954 #if 0
2955   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2956     base_key_graphic = EL_EM_KEY_1;
2957 #endif
2958 
2959 #if 0
2960   pos->width = 4 * MINI_TILEX;
2961 #endif
2962 
2963 #if 1
2964   for (i = 0; i < MAX_NUM_KEYS; i++)
2965 #else
2966   /* currently only 4 of 8 possible keys are displayed */
2967   for (i = 0; i < STD_NUM_KEYS; i++)
2968 #endif
2969   {
2970 #if 1
2971     struct TextPosInfo *pos = &game.panel.key[i];
2972 #endif
2973     int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2974     int src_y = DOOR_GFX_PAGEY1 + 123;
2975 #if 1
2976     int dst_x = PANEL_XPOS(pos);
2977     int dst_y = PANEL_YPOS(pos);
2978 #else
2979     int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2980     int dst_y = PANEL_YPOS(pos);
2981 #endif
2982 
2983 #if 1
2984     int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2985 		   level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2986 		   EL_KEY_1) + i;
2987     int graphic = el2edimg(element);
2988 #endif
2989 
2990 #if 1
2991     if (PANEL_DEACTIVATED(pos))
2992       continue;
2993 #endif
2994 
2995 #if 0
2996     /* masked blit with tiles from half-size scaled bitmap does not work yet
2997        (no mask bitmap created for these sizes after loading and scaling) --
2998        solution: load without creating mask, scale, then create final mask */
2999 
3000     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3001 	       MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3002 
3003     if (key[i])
3004     {
3005 #if 0
3006       int graphic = el2edimg(base_key_graphic + i);
3007 #endif
3008       Bitmap *src_bitmap;
3009       int src_x, src_y;
3010 
3011       getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
3012 
3013       SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
3014 		    dst_x - src_x, dst_y - src_y);
3015       BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
3016 		       dst_x, dst_y);
3017     }
3018 #else
3019 #if 1
3020     if (key[i])
3021       DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
3022     else
3023       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3024 		 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3025 #else
3026     if (key[i])
3027       DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3028     else
3029       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3030 		 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3031 #endif
3032 #endif
3033   }
3034 }
3035 
3036 #else
3037 
DrawGameValue_Emeralds(int value)3038 void DrawGameValue_Emeralds(int value)
3039 {
3040   int font_nr = FONT_TEXT_2;
3041   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3042 
3043   if (PANEL_DEACTIVATED(game.panel.gems))
3044     return;
3045 
3046   DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3047 }
3048 
DrawGameValue_Dynamite(int value)3049 void DrawGameValue_Dynamite(int value)
3050 {
3051   int font_nr = FONT_TEXT_2;
3052   int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3053 
3054   if (PANEL_DEACTIVATED(game.panel.inventory_count))
3055     return;
3056 
3057   DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3058 }
3059 
DrawGameValue_Score(int value)3060 void DrawGameValue_Score(int value)
3061 {
3062   int font_nr = FONT_TEXT_2;
3063   int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3064 
3065   if (PANEL_DEACTIVATED(game.panel.score))
3066     return;
3067 
3068   DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3069 }
3070 
DrawGameValue_Time(int value)3071 void DrawGameValue_Time(int value)
3072 {
3073   int font1_nr = FONT_TEXT_2;
3074 #if 1
3075   int font2_nr = FONT_TEXT_1;
3076 #else
3077   int font2_nr = FONT_LEVEL_NUMBER;
3078 #endif
3079   int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3080   int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3081 
3082   if (PANEL_DEACTIVATED(game.panel.time))
3083     return;
3084 
3085   /* clear background if value just changed its size */
3086   if (value == 999 || value == 1000)
3087     ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3088 
3089   if (value < 1000)
3090     DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3091   else
3092     DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3093 }
3094 
DrawGameValue_Level(int value)3095 void DrawGameValue_Level(int value)
3096 {
3097   int font1_nr = FONT_TEXT_2;
3098 #if 1
3099   int font2_nr = FONT_TEXT_1;
3100 #else
3101   int font2_nr = FONT_LEVEL_NUMBER;
3102 #endif
3103 
3104   if (PANEL_DEACTIVATED(game.panel.level))
3105     return;
3106 
3107   if (level_nr < 100)
3108     DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3109   else
3110     DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3111 }
3112 
DrawGameValue_Keys(int key[MAX_NUM_KEYS])3113 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3114 {
3115   int base_key_graphic = EL_KEY_1;
3116   int i;
3117 
3118   if (PANEL_DEACTIVATED(game.panel.keys))
3119     return;
3120 
3121   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3122     base_key_graphic = EL_EM_KEY_1;
3123 
3124   /* currently only 4 of 8 possible keys are displayed */
3125   for (i = 0; i < STD_NUM_KEYS; i++)
3126   {
3127     int x = XX_KEYS + i * MINI_TILEX;
3128     int y = YY_KEYS;
3129 
3130     if (key[i])
3131       DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3132     else
3133       BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3134 		 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3135   }
3136 }
3137 
3138 #endif
3139 
DrawAllGameValues(int emeralds,int dynamite,int score,int time,int key_bits)3140 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3141 		       int key_bits)
3142 {
3143   int key[MAX_NUM_KEYS];
3144   int i;
3145 
3146   /* prevent EM engine from updating time/score values parallel to GameWon() */
3147   if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3148       local_player->LevelSolved)
3149     return;
3150 
3151   for (i = 0; i < MAX_NUM_KEYS; i++)
3152     key[i] = key_bits & (1 << i);
3153 
3154   DrawGameValue_Level(level_nr);
3155 
3156   DrawGameValue_Emeralds(emeralds);
3157   DrawGameValue_Dynamite(dynamite);
3158   DrawGameValue_Score(score);
3159   DrawGameValue_Time(time);
3160 
3161   DrawGameValue_Keys(key);
3162 }
3163 
UpdateGameDoorValues()3164 void UpdateGameDoorValues()
3165 {
3166   UpdateGameControlValues();
3167 }
3168 
DrawGameDoorValues()3169 void DrawGameDoorValues()
3170 {
3171   DisplayGameControlValues();
3172 }
3173 
DrawGameDoorValues_OLD()3174 void DrawGameDoorValues_OLD()
3175 {
3176   int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3177   int dynamite_value = 0;
3178   int score_value = (local_player->LevelSolved ? local_player->score_final :
3179 		     local_player->score);
3180   int gems_value = local_player->gems_still_needed;
3181   int key_bits = 0;
3182   int i, j;
3183 
3184   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3185   {
3186     DrawGameDoorValues_EM();
3187 
3188     return;
3189   }
3190 
3191   if (game.centered_player_nr == -1)
3192   {
3193     for (i = 0; i < MAX_PLAYERS; i++)
3194     {
3195       for (j = 0; j < MAX_NUM_KEYS; j++)
3196 	if (stored_player[i].key[j])
3197 	  key_bits |= (1 << j);
3198 
3199       dynamite_value += stored_player[i].inventory_size;
3200     }
3201   }
3202   else
3203   {
3204     int player_nr = game.centered_player_nr;
3205 
3206     for (i = 0; i < MAX_NUM_KEYS; i++)
3207       if (stored_player[player_nr].key[i])
3208 	key_bits |= (1 << i);
3209 
3210     dynamite_value = stored_player[player_nr].inventory_size;
3211   }
3212 
3213   DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3214 		    key_bits);
3215 }
3216 
3217 
3218 /*
3219   =============================================================================
3220   InitGameEngine()
3221   -----------------------------------------------------------------------------
3222   initialize game engine due to level / tape version number
3223   =============================================================================
3224 */
3225 
InitGameEngine()3226 static void InitGameEngine()
3227 {
3228   int i, j, k, l, x, y;
3229 
3230   /* set game engine from tape file when re-playing, else from level file */
3231   game.engine_version = (tape.playing ? tape.engine_version :
3232 			 level.game_version);
3233 
3234   /* ---------------------------------------------------------------------- */
3235   /* set flags for bugs and changes according to active game engine version */
3236   /* ---------------------------------------------------------------------- */
3237 
3238   /*
3239     Summary of bugfix/change:
3240     Fixed handling for custom elements that change when pushed by the player.
3241 
3242     Fixed/changed in version:
3243     3.1.0
3244 
3245     Description:
3246     Before 3.1.0, custom elements that "change when pushing" changed directly
3247     after the player started pushing them (until then handled in "DigField()").
3248     Since 3.1.0, these custom elements are not changed until the "pushing"
3249     move of the element is finished (now handled in "ContinueMoving()").
3250 
3251     Affected levels/tapes:
3252     The first condition is generally needed for all levels/tapes before version
3253     3.1.0, which might use the old behaviour before it was changed; known tapes
3254     that are affected are some tapes from the level set "Walpurgis Gardens" by
3255     Jamie Cullen.
3256     The second condition is an exception from the above case and is needed for
3257     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3258     above (including some development versions of 3.1.0), but before it was
3259     known that this change would break tapes like the above and was fixed in
3260     3.1.1, so that the changed behaviour was active although the engine version
3261     while recording maybe was before 3.1.0. There is at least one tape that is
3262     affected by this exception, which is the tape for the one-level set "Bug
3263     Machine" by Juergen Bonhagen.
3264   */
3265 
3266   game.use_change_when_pushing_bug =
3267     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3268      !(tape.playing &&
3269        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3270        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3271 
3272   /*
3273     Summary of bugfix/change:
3274     Fixed handling for blocking the field the player leaves when moving.
3275 
3276     Fixed/changed in version:
3277     3.1.1
3278 
3279     Description:
3280     Before 3.1.1, when "block last field when moving" was enabled, the field
3281     the player is leaving when moving was blocked for the time of the move,
3282     and was directly unblocked afterwards. This resulted in the last field
3283     being blocked for exactly one less than the number of frames of one player
3284     move. Additionally, even when blocking was disabled, the last field was
3285     blocked for exactly one frame.
3286     Since 3.1.1, due to changes in player movement handling, the last field
3287     is not blocked at all when blocking is disabled. When blocking is enabled,
3288     the last field is blocked for exactly the number of frames of one player
3289     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3290     last field is blocked for exactly one more than the number of frames of
3291     one player move.
3292 
3293     Affected levels/tapes:
3294     (!!! yet to be determined -- probably many !!!)
3295   */
3296 
3297   game.use_block_last_field_bug =
3298     (game.engine_version < VERSION_IDENT(3,1,1,0));
3299 
3300   /*
3301     Summary of bugfix/change:
3302     Changed behaviour of CE changes with multiple changes per single frame.
3303 
3304     Fixed/changed in version:
3305     3.2.0-6
3306 
3307     Description:
3308     Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3309     This resulted in race conditions where CEs seem to behave strange in some
3310     situations (where triggered CE changes were just skipped because there was
3311     already a CE change on that tile in the playfield in that engine frame).
3312     Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3313     (The number of changes per frame must be limited in any case, because else
3314     it is easily possible to define CE changes that would result in an infinite
3315     loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3316     should be set large enough so that it would only be reached in cases where
3317     the corresponding CE change conditions run into a loop. Therefore, it seems
3318     to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3319     maximal number of change pages for custom elements.)
3320 
3321     Affected levels/tapes:
3322     Probably many.
3323   */
3324 
3325 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3326   game.max_num_changes_per_frame = 1;
3327 #else
3328   game.max_num_changes_per_frame =
3329     (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3330 #endif
3331 
3332   /* ---------------------------------------------------------------------- */
3333 
3334   /* default scan direction: scan playfield from top/left to bottom/right */
3335   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3336 
3337   /* dynamically adjust element properties according to game engine version */
3338   InitElementPropertiesEngine(game.engine_version);
3339 
3340 #if 0
3341   printf("level %d: level version == %06d\n", level_nr, level.game_version);
3342   printf("          tape version == %06d [%s] [file: %06d]\n",
3343 	 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3344 	 tape.file_version);
3345   printf("       => game.engine_version == %06d\n", game.engine_version);
3346 #endif
3347 
3348   /* ---------- initialize player's initial move delay --------------------- */
3349 
3350   /* dynamically adjust player properties according to level information */
3351   for (i = 0; i < MAX_PLAYERS; i++)
3352     game.initial_move_delay_value[i] =
3353       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3354 
3355   /* dynamically adjust player properties according to game engine version */
3356   for (i = 0; i < MAX_PLAYERS; i++)
3357     game.initial_move_delay[i] =
3358       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3359        game.initial_move_delay_value[i] : 0);
3360 
3361   /* ---------- initialize player's initial push delay --------------------- */
3362 
3363   /* dynamically adjust player properties according to game engine version */
3364   game.initial_push_delay_value =
3365     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3366 
3367   /* ---------- initialize changing elements ------------------------------- */
3368 
3369   /* initialize changing elements information */
3370   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3371   {
3372     struct ElementInfo *ei = &element_info[i];
3373 
3374     /* this pointer might have been changed in the level editor */
3375     ei->change = &ei->change_page[0];
3376 
3377     if (!IS_CUSTOM_ELEMENT(i))
3378     {
3379       ei->change->target_element = EL_EMPTY_SPACE;
3380       ei->change->delay_fixed = 0;
3381       ei->change->delay_random = 0;
3382       ei->change->delay_frames = 1;
3383     }
3384 
3385     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3386     {
3387       ei->has_change_event[j] = FALSE;
3388 
3389       ei->event_page_nr[j] = 0;
3390       ei->event_page[j] = &ei->change_page[0];
3391     }
3392   }
3393 
3394   /* add changing elements from pre-defined list */
3395   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3396   {
3397     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3398     struct ElementInfo *ei = &element_info[ch_delay->element];
3399 
3400     ei->change->target_element       = ch_delay->target_element;
3401     ei->change->delay_fixed          = ch_delay->change_delay;
3402 
3403     ei->change->pre_change_function  = ch_delay->pre_change_function;
3404     ei->change->change_function      = ch_delay->change_function;
3405     ei->change->post_change_function = ch_delay->post_change_function;
3406 
3407     ei->change->can_change = TRUE;
3408     ei->change->can_change_or_has_action = TRUE;
3409 
3410     ei->has_change_event[CE_DELAY] = TRUE;
3411 
3412     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3413     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3414   }
3415 
3416   /* ---------- initialize internal run-time variables --------------------- */
3417 
3418   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3419   {
3420     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3421 
3422     for (j = 0; j < ei->num_change_pages; j++)
3423     {
3424       ei->change_page[j].can_change_or_has_action =
3425 	(ei->change_page[j].can_change |
3426 	 ei->change_page[j].has_action);
3427     }
3428   }
3429 
3430   /* add change events from custom element configuration */
3431   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3432   {
3433     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3434 
3435     for (j = 0; j < ei->num_change_pages; j++)
3436     {
3437       if (!ei->change_page[j].can_change_or_has_action)
3438 	continue;
3439 
3440       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3441       {
3442 	/* only add event page for the first page found with this event */
3443 	if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3444 	{
3445 	  ei->has_change_event[k] = TRUE;
3446 
3447 	  ei->event_page_nr[k] = j;
3448 	  ei->event_page[k] = &ei->change_page[j];
3449 	}
3450       }
3451     }
3452   }
3453 
3454 #if 1
3455   /* ---------- initialize reference elements in change conditions --------- */
3456 
3457   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3458   {
3459     int element = EL_CUSTOM_START + i;
3460     struct ElementInfo *ei = &element_info[element];
3461 
3462     for (j = 0; j < ei->num_change_pages; j++)
3463     {
3464       int trigger_element = ei->change_page[j].initial_trigger_element;
3465 
3466       if (trigger_element >= EL_PREV_CE_8 &&
3467 	  trigger_element <= EL_NEXT_CE_8)
3468 	trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3469 
3470       ei->change_page[j].trigger_element = trigger_element;
3471     }
3472   }
3473 #endif
3474 
3475   /* ---------- initialize run-time trigger player and element ------------- */
3476 
3477   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3478   {
3479     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3480 
3481     for (j = 0; j < ei->num_change_pages; j++)
3482     {
3483       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3484       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3485       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3486       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3487       ei->change_page[j].actual_trigger_ce_value = 0;
3488       ei->change_page[j].actual_trigger_ce_score = 0;
3489     }
3490   }
3491 
3492   /* ---------- initialize trigger events ---------------------------------- */
3493 
3494   /* initialize trigger events information */
3495   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3496     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3497       trigger_events[i][j] = FALSE;
3498 
3499   /* add trigger events from element change event properties */
3500   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3501   {
3502     struct ElementInfo *ei = &element_info[i];
3503 
3504     for (j = 0; j < ei->num_change_pages; j++)
3505     {
3506       if (!ei->change_page[j].can_change_or_has_action)
3507 	continue;
3508 
3509       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3510       {
3511 	int trigger_element = ei->change_page[j].trigger_element;
3512 
3513 	for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3514 	{
3515 	  if (ei->change_page[j].has_event[k])
3516 	  {
3517 	    if (IS_GROUP_ELEMENT(trigger_element))
3518 	    {
3519 	      struct ElementGroupInfo *group =
3520 		element_info[trigger_element].group;
3521 
3522 	      for (l = 0; l < group->num_elements_resolved; l++)
3523 		trigger_events[group->element_resolved[l]][k] = TRUE;
3524 	    }
3525 	    else if (trigger_element == EL_ANY_ELEMENT)
3526 	      for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3527 		trigger_events[l][k] = TRUE;
3528 	    else
3529 	      trigger_events[trigger_element][k] = TRUE;
3530 	  }
3531 	}
3532       }
3533     }
3534   }
3535 
3536   /* ---------- initialize push delay -------------------------------------- */
3537 
3538   /* initialize push delay values to default */
3539   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3540   {
3541     if (!IS_CUSTOM_ELEMENT(i))
3542     {
3543       /* set default push delay values (corrected since version 3.0.7-1) */
3544       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3545       {
3546 	element_info[i].push_delay_fixed = 2;
3547 	element_info[i].push_delay_random = 8;
3548       }
3549       else
3550       {
3551 	element_info[i].push_delay_fixed = 8;
3552 	element_info[i].push_delay_random = 8;
3553       }
3554     }
3555   }
3556 
3557   /* set push delay value for certain elements from pre-defined list */
3558   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3559   {
3560     int e = push_delay_list[i].element;
3561 
3562     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3563     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3564   }
3565 
3566   /* set push delay value for Supaplex elements for newer engine versions */
3567   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3568   {
3569     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3570     {
3571       if (IS_SP_ELEMENT(i))
3572       {
3573 	/* set SP push delay to just enough to push under a falling zonk */
3574 	int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3575 
3576 	element_info[i].push_delay_fixed  = delay;
3577 	element_info[i].push_delay_random = 0;
3578       }
3579     }
3580   }
3581 
3582   /* ---------- initialize move stepsize ----------------------------------- */
3583 
3584   /* initialize move stepsize values to default */
3585   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3586     if (!IS_CUSTOM_ELEMENT(i))
3587       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3588 
3589   /* set move stepsize value for certain elements from pre-defined list */
3590   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3591   {
3592     int e = move_stepsize_list[i].element;
3593 
3594     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3595   }
3596 
3597   /* ---------- initialize collect score ----------------------------------- */
3598 
3599   /* initialize collect score values for custom elements from initial value */
3600   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3601     if (IS_CUSTOM_ELEMENT(i))
3602       element_info[i].collect_score = element_info[i].collect_score_initial;
3603 
3604   /* ---------- initialize collect count ----------------------------------- */
3605 
3606   /* initialize collect count values for non-custom elements */
3607   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3608     if (!IS_CUSTOM_ELEMENT(i))
3609       element_info[i].collect_count_initial = 0;
3610 
3611   /* add collect count values for all elements from pre-defined list */
3612   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3613     element_info[collect_count_list[i].element].collect_count_initial =
3614       collect_count_list[i].count;
3615 
3616   /* ---------- initialize access direction -------------------------------- */
3617 
3618   /* initialize access direction values to default (access from every side) */
3619   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3620     if (!IS_CUSTOM_ELEMENT(i))
3621       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3622 
3623   /* set access direction value for certain elements from pre-defined list */
3624   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3625     element_info[access_direction_list[i].element].access_direction =
3626       access_direction_list[i].direction;
3627 
3628   /* ---------- initialize explosion content ------------------------------- */
3629   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3630   {
3631     if (IS_CUSTOM_ELEMENT(i))
3632       continue;
3633 
3634     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3635     {
3636       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3637 
3638       element_info[i].content.e[x][y] =
3639 	(i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3640 	 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3641 	 i == EL_PLAYER_3 ? EL_EMERALD :
3642 	 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3643 	 i == EL_MOLE ? EL_EMERALD_RED :
3644 	 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3645 	 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3646 	 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3647 	 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3648 	 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3649 	 i == EL_WALL_EMERALD ? EL_EMERALD :
3650 	 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3651 	 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3652 	 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3653 	 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3654 	 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3655 	 i == EL_WALL_PEARL ? EL_PEARL :
3656 	 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3657 	 EL_EMPTY);
3658     }
3659   }
3660 
3661   /* ---------- initialize recursion detection ------------------------------ */
3662   recursion_loop_depth = 0;
3663   recursion_loop_detected = FALSE;
3664   recursion_loop_element = EL_UNDEFINED;
3665 
3666   /* ---------- initialize graphics engine ---------------------------------- */
3667   game.scroll_delay_value =
3668     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3669      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3670   game.scroll_delay_value =
3671     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3672 }
3673 
get_num_special_action(int element,int action_first,int action_last)3674 int get_num_special_action(int element, int action_first, int action_last)
3675 {
3676   int num_special_action = 0;
3677   int i, j;
3678 
3679   for (i = action_first; i <= action_last; i++)
3680   {
3681     boolean found = FALSE;
3682 
3683     for (j = 0; j < NUM_DIRECTIONS; j++)
3684       if (el_act_dir2img(element, i, j) !=
3685 	  el_act_dir2img(element, ACTION_DEFAULT, j))
3686 	found = TRUE;
3687 
3688     if (found)
3689       num_special_action++;
3690     else
3691       break;
3692   }
3693 
3694   return num_special_action;
3695 }
3696 
3697 
3698 /*
3699   =============================================================================
3700   InitGame()
3701   -----------------------------------------------------------------------------
3702   initialize and start new game
3703   =============================================================================
3704 */
3705 
InitGame()3706 void InitGame()
3707 {
3708   boolean emulate_bd = TRUE;	/* unless non-BOULDERDASH elements found */
3709   boolean emulate_sb = TRUE;	/* unless non-SOKOBAN     elements found */
3710   boolean emulate_sp = TRUE;	/* unless non-SUPAPLEX    elements found */
3711 #if 0
3712   boolean do_fading = (game_status == GAME_MODE_MAIN);
3713 #endif
3714 #if 1
3715   int initial_move_dir = MV_DOWN;
3716 #else
3717   int initial_move_dir = MV_NONE;
3718 #endif
3719   int i, j, x, y;
3720 
3721   game_status = GAME_MODE_PLAYING;
3722 
3723 #if 1
3724   /* needed if different viewport properties defined for playing */
3725   ChangeViewportPropertiesIfNeeded();
3726 #endif
3727 
3728 #if 1
3729   DrawCompleteVideoDisplay();
3730 #endif
3731 
3732   InitGameEngine();
3733   InitGameControlValues();
3734 
3735   /* don't play tapes over network */
3736   network_playing = (options.network && !tape.playing);
3737 
3738   for (i = 0; i < MAX_PLAYERS; i++)
3739   {
3740     struct PlayerInfo *player = &stored_player[i];
3741 
3742     player->index_nr = i;
3743     player->index_bit = (1 << i);
3744     player->element_nr = EL_PLAYER_1 + i;
3745 
3746     player->present = FALSE;
3747     player->active = FALSE;
3748     player->mapped = FALSE;
3749 
3750     player->killed = FALSE;
3751     player->reanimated = FALSE;
3752 
3753     player->action = 0;
3754     player->effective_action = 0;
3755     player->programmed_action = 0;
3756 
3757     player->score = 0;
3758     player->score_final = 0;
3759 
3760     player->gems_still_needed = level.gems_needed;
3761     player->sokobanfields_still_needed = 0;
3762     player->lights_still_needed = 0;
3763     player->friends_still_needed = 0;
3764 
3765     for (j = 0; j < MAX_NUM_KEYS; j++)
3766       player->key[j] = FALSE;
3767 
3768     player->num_white_keys = 0;
3769 
3770     player->dynabomb_count = 0;
3771     player->dynabomb_size = 1;
3772     player->dynabombs_left = 0;
3773     player->dynabomb_xl = FALSE;
3774 
3775     player->MovDir = initial_move_dir;
3776     player->MovPos = 0;
3777     player->GfxPos = 0;
3778     player->GfxDir = initial_move_dir;
3779     player->GfxAction = ACTION_DEFAULT;
3780     player->Frame = 0;
3781     player->StepFrame = 0;
3782 
3783     player->initial_element = player->element_nr;
3784     player->artwork_element =
3785       (level.use_artwork_element[i] ? level.artwork_element[i] :
3786        player->element_nr);
3787     player->use_murphy = FALSE;
3788 
3789     player->block_last_field = FALSE;	/* initialized in InitPlayerField() */
3790     player->block_delay_adjustment = 0;	/* initialized in InitPlayerField() */
3791 
3792     player->gravity = level.initial_player_gravity[i];
3793 
3794     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3795 
3796     player->actual_frame_counter = 0;
3797 
3798     player->step_counter = 0;
3799 
3800     player->last_move_dir = initial_move_dir;
3801 
3802     player->is_active = FALSE;
3803 
3804     player->is_waiting = FALSE;
3805     player->is_moving = FALSE;
3806     player->is_auto_moving = FALSE;
3807     player->is_digging = FALSE;
3808     player->is_snapping = FALSE;
3809     player->is_collecting = FALSE;
3810     player->is_pushing = FALSE;
3811     player->is_switching = FALSE;
3812     player->is_dropping = FALSE;
3813     player->is_dropping_pressed = FALSE;
3814 
3815     player->is_bored = FALSE;
3816     player->is_sleeping = FALSE;
3817 
3818     player->frame_counter_bored = -1;
3819     player->frame_counter_sleeping = -1;
3820 
3821     player->anim_delay_counter = 0;
3822     player->post_delay_counter = 0;
3823 
3824     player->dir_waiting = initial_move_dir;
3825     player->action_waiting = ACTION_DEFAULT;
3826     player->last_action_waiting = ACTION_DEFAULT;
3827     player->special_action_bored = ACTION_DEFAULT;
3828     player->special_action_sleeping = ACTION_DEFAULT;
3829 
3830     player->switch_x = -1;
3831     player->switch_y = -1;
3832 
3833     player->drop_x = -1;
3834     player->drop_y = -1;
3835 
3836     player->show_envelope = 0;
3837 
3838     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3839 
3840     player->push_delay       = -1;	/* initialized when pushing starts */
3841     player->push_delay_value = game.initial_push_delay_value;
3842 
3843     player->drop_delay = 0;
3844     player->drop_pressed_delay = 0;
3845 
3846     player->last_jx = -1;
3847     player->last_jy = -1;
3848     player->jx = -1;
3849     player->jy = -1;
3850 
3851     player->shield_normal_time_left = 0;
3852     player->shield_deadly_time_left = 0;
3853 
3854     player->inventory_infinite_element = EL_UNDEFINED;
3855     player->inventory_size = 0;
3856 
3857     if (level.use_initial_inventory[i])
3858     {
3859       for (j = 0; j < level.initial_inventory_size[i]; j++)
3860       {
3861 	int element = level.initial_inventory_content[i][j];
3862 	int collect_count = element_info[element].collect_count_initial;
3863 	int k;
3864 
3865 	if (!IS_CUSTOM_ELEMENT(element))
3866 	  collect_count = 1;
3867 
3868 	if (collect_count == 0)
3869 	  player->inventory_infinite_element = element;
3870 	else
3871 	  for (k = 0; k < collect_count; k++)
3872 	    if (player->inventory_size < MAX_INVENTORY_SIZE)
3873 	      player->inventory_element[player->inventory_size++] = element;
3874       }
3875     }
3876 
3877     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3878     SnapField(player, 0, 0);
3879 
3880     player->LevelSolved = FALSE;
3881     player->GameOver = FALSE;
3882 
3883     player->LevelSolved_GameWon = FALSE;
3884     player->LevelSolved_GameEnd = FALSE;
3885     player->LevelSolved_PanelOff = FALSE;
3886     player->LevelSolved_SaveTape = FALSE;
3887     player->LevelSolved_SaveScore = FALSE;
3888     player->LevelSolved_CountingTime = 0;
3889     player->LevelSolved_CountingScore = 0;
3890 
3891     map_player_action[i] = i;
3892   }
3893 
3894   network_player_action_received = FALSE;
3895 
3896 #if defined(NETWORK_AVALIABLE)
3897   /* initial null action */
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900 #endif
3901 
3902   ZX = ZY = -1;
3903   ExitX = ExitY = -1;
3904 
3905   FrameCounter = 0;
3906   TimeFrames = 0;
3907   TimePlayed = 0;
3908   TimeLeft = level.time;
3909   TapeTime = 0;
3910 
3911   ScreenMovDir = MV_NONE;
3912   ScreenMovPos = 0;
3913   ScreenGfxPos = 0;
3914 
3915   ScrollStepSize = 0;	/* will be correctly initialized by ScrollScreen() */
3916 
3917   AllPlayersGone = FALSE;
3918 
3919   game.yamyam_content_nr = 0;
3920   game.robot_wheel_active = FALSE;
3921   game.magic_wall_active = FALSE;
3922   game.magic_wall_time_left = 0;
3923   game.light_time_left = 0;
3924   game.timegate_time_left = 0;
3925   game.switchgate_pos = 0;
3926   game.wind_direction = level.wind_direction_initial;
3927 
3928 #if !USE_PLAYER_GRAVITY
3929   game.gravity = FALSE;
3930   game.explosions_delayed = TRUE;
3931 #endif
3932 
3933   game.lenses_time_left = 0;
3934   game.magnify_time_left = 0;
3935 
3936   game.ball_state = level.ball_state_initial;
3937   game.ball_content_nr = 0;
3938 
3939   game.envelope_active = FALSE;
3940 
3941   /* set focus to local player for network games, else to all players */
3942   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3943   game.centered_player_nr_next = game.centered_player_nr;
3944   game.set_centered_player = FALSE;
3945 
3946   if (network_playing && tape.recording)
3947   {
3948     /* store client dependent player focus when recording network games */
3949     tape.centered_player_nr_next = game.centered_player_nr_next;
3950     tape.set_centered_player = TRUE;
3951   }
3952 
3953   for (i = 0; i < NUM_BELTS; i++)
3954   {
3955     game.belt_dir[i] = MV_NONE;
3956     game.belt_dir_nr[i] = 3;		/* not moving, next moving left */
3957   }
3958 
3959   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3960     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3961 
3962   SCAN_PLAYFIELD(x, y)
3963   {
3964     Feld[x][y] = level.field[x][y];
3965     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3966     ChangeDelay[x][y] = 0;
3967     ChangePage[x][y] = -1;
3968 #if USE_NEW_CUSTOM_VALUE
3969     CustomValue[x][y] = 0;		/* initialized in InitField() */
3970 #endif
3971     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3972     AmoebaNr[x][y] = 0;
3973     WasJustMoving[x][y] = 0;
3974     WasJustFalling[x][y] = 0;
3975     CheckCollision[x][y] = 0;
3976     CheckImpact[x][y] = 0;
3977     Stop[x][y] = FALSE;
3978     Pushed[x][y] = FALSE;
3979 
3980     ChangeCount[x][y] = 0;
3981     ChangeEvent[x][y] = -1;
3982 
3983     ExplodePhase[x][y] = 0;
3984     ExplodeDelay[x][y] = 0;
3985     ExplodeField[x][y] = EX_TYPE_NONE;
3986 
3987     RunnerVisit[x][y] = 0;
3988     PlayerVisit[x][y] = 0;
3989 
3990     GfxFrame[x][y] = 0;
3991     GfxRandom[x][y] = INIT_GFX_RANDOM();
3992     GfxElement[x][y] = EL_UNDEFINED;
3993     GfxAction[x][y] = ACTION_DEFAULT;
3994     GfxDir[x][y] = MV_NONE;
3995     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3996   }
3997 
3998   SCAN_PLAYFIELD(x, y)
3999   {
4000     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
4001       emulate_bd = FALSE;
4002     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
4003       emulate_sb = FALSE;
4004     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
4005       emulate_sp = FALSE;
4006 
4007     InitField(x, y, TRUE);
4008 
4009     ResetGfxAnimation(x, y);
4010   }
4011 
4012   InitBeltMovement();
4013 
4014   for (i = 0; i < MAX_PLAYERS; i++)
4015   {
4016     struct PlayerInfo *player = &stored_player[i];
4017 
4018     /* set number of special actions for bored and sleeping animation */
4019     player->num_special_action_bored =
4020       get_num_special_action(player->artwork_element,
4021 			     ACTION_BORING_1, ACTION_BORING_LAST);
4022     player->num_special_action_sleeping =
4023       get_num_special_action(player->artwork_element,
4024 			     ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4025   }
4026 
4027   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4028 		    emulate_sb ? EMU_SOKOBAN :
4029 		    emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4030 
4031 #if USE_NEW_ALL_SLIPPERY
4032   /* initialize type of slippery elements */
4033   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4034   {
4035     if (!IS_CUSTOM_ELEMENT(i))
4036     {
4037       /* default: elements slip down either to the left or right randomly */
4038       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4039 
4040       /* SP style elements prefer to slip down on the left side */
4041       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4042 	element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4043 
4044       /* BD style elements prefer to slip down on the left side */
4045       if (game.emulation == EMU_BOULDERDASH)
4046 	element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4047     }
4048   }
4049 #endif
4050 
4051   /* initialize explosion and ignition delay */
4052   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4053   {
4054     if (!IS_CUSTOM_ELEMENT(i))
4055     {
4056       int num_phase = 8;
4057       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4058 		    game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4059 		   game.emulation == EMU_SUPAPLEX ? 3 : 2);
4060       int last_phase = (num_phase + 1) * delay;
4061       int half_phase = (num_phase / 2) * delay;
4062 
4063       element_info[i].explosion_delay = last_phase - 1;
4064       element_info[i].ignition_delay = half_phase;
4065 
4066       if (i == EL_BLACK_ORB)
4067 	element_info[i].ignition_delay = 1;
4068     }
4069 
4070 #if 0
4071     if (element_info[i].explosion_delay < 1)	/* !!! check again !!! */
4072       element_info[i].explosion_delay = 1;
4073 
4074     if (element_info[i].ignition_delay < 1)	/* !!! check again !!! */
4075       element_info[i].ignition_delay = 1;
4076 #endif
4077   }
4078 
4079   /* correct non-moving belts to start moving left */
4080   for (i = 0; i < NUM_BELTS; i++)
4081     if (game.belt_dir[i] == MV_NONE)
4082       game.belt_dir_nr[i] = 3;		/* not moving, next moving left */
4083 
4084 #if USE_NEW_PLAYER_ASSIGNMENTS
4085   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4086   /* choose default local player */
4087   local_player = &stored_player[0];
4088 
4089   for (i = 0; i < MAX_PLAYERS; i++)
4090     stored_player[i].connected = FALSE;
4091 
4092   local_player->connected = TRUE;
4093   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4094 
4095   if (tape.playing)
4096   {
4097     /* try to guess locally connected team mode players (needed for correct
4098        assignment of player figures from level to locally playing players) */
4099 
4100     for (i = 0; i < MAX_PLAYERS; i++)
4101       if (tape.player_participates[i])
4102 	stored_player[i].connected = TRUE;
4103   }
4104   else if (setup.team_mode && !options.network)
4105   {
4106     /* try to guess locally connected team mode players (needed for correct
4107        assignment of player figures from level to locally playing players) */
4108 
4109     for (i = 0; i < MAX_PLAYERS; i++)
4110       if (setup.input[i].use_joystick ||
4111 	  setup.input[i].key.left != KSYM_UNDEFINED)
4112 	stored_player[i].connected = TRUE;
4113   }
4114 
4115 #if 0
4116   for (i = 0; i < MAX_PLAYERS; i++)
4117     printf("::: player %d: %s\n", i,
4118 	   (stored_player[i].connected ? "connected" : "not connected"));
4119 
4120   for (i = 0; i < MAX_PLAYERS; i++)
4121     printf("::: player %d: %s\n", i,
4122 	   (stored_player[i].present ? "present" : "not present"));
4123 #endif
4124 
4125   /* check if any connected player was not found in playfield */
4126   for (i = 0; i < MAX_PLAYERS; i++)
4127   {
4128     struct PlayerInfo *player = &stored_player[i];
4129 
4130     if (player->connected && !player->present)
4131     {
4132       struct PlayerInfo *field_player = NULL;
4133 
4134 #if 0
4135       printf("::: looking for field player for player %d ...\n", i);
4136 #endif
4137 
4138       /* assign first free player found that is present in the playfield */
4139 
4140       /* first try: look for unmapped playfield player that is not connected */
4141       if (field_player == NULL)
4142 	for (j = 0; j < MAX_PLAYERS; j++)
4143 	  if (stored_player[j].present &&
4144 	      !stored_player[j].mapped &&
4145 	      !stored_player[j].connected)
4146 	    field_player = &stored_player[j];
4147 
4148       /* second try: look for *any* unmapped playfield player */
4149       if (field_player == NULL)
4150 	for (j = 0; j < MAX_PLAYERS; j++)
4151 	  if (stored_player[j].present &&
4152 	      !stored_player[j].mapped)
4153 	    field_player = &stored_player[j];
4154 
4155       if (field_player != NULL)
4156       {
4157 	int jx = field_player->jx, jy = field_player->jy;
4158 
4159 #if 0
4160 	printf("::: found player figure %d\n", field_player->index_nr);
4161 #endif
4162 
4163 	player->present = FALSE;
4164 	player->active = FALSE;
4165 
4166 	field_player->present = TRUE;
4167 	field_player->active = TRUE;
4168 
4169 	/*
4170 	player->initial_element = field_player->initial_element;
4171 	player->artwork_element = field_player->artwork_element;
4172 
4173 	player->block_last_field       = field_player->block_last_field;
4174 	player->block_delay_adjustment = field_player->block_delay_adjustment;
4175 	*/
4176 
4177 	StorePlayer[jx][jy] = field_player->element_nr;
4178 
4179 	field_player->jx = field_player->last_jx = jx;
4180 	field_player->jy = field_player->last_jy = jy;
4181 
4182 	if (local_player == player)
4183 	  local_player = field_player;
4184 
4185 	map_player_action[field_player->index_nr] = i;
4186 
4187 	field_player->mapped = TRUE;
4188 
4189 #if 0
4190 	printf("::: map_player_action[%d] == %d\n",
4191 	       field_player->index_nr, i);
4192 #endif
4193       }
4194     }
4195 
4196     if (player->connected && player->present)
4197       player->mapped = TRUE;
4198   }
4199 
4200 #else
4201 
4202   /* check if any connected player was not found in playfield */
4203   for (i = 0; i < MAX_PLAYERS; i++)
4204   {
4205     struct PlayerInfo *player = &stored_player[i];
4206 
4207     if (player->connected && !player->present)
4208     {
4209       for (j = 0; j < MAX_PLAYERS; j++)
4210       {
4211 	struct PlayerInfo *field_player = &stored_player[j];
4212 	int jx = field_player->jx, jy = field_player->jy;
4213 
4214 	/* assign first free player found that is present in the playfield */
4215 	if (field_player->present && !field_player->connected)
4216 	{
4217 	  player->present = TRUE;
4218 	  player->active = TRUE;
4219 
4220 	  field_player->present = FALSE;
4221 	  field_player->active = FALSE;
4222 
4223 	  player->initial_element = field_player->initial_element;
4224 	  player->artwork_element = field_player->artwork_element;
4225 
4226 	  player->block_last_field       = field_player->block_last_field;
4227 	  player->block_delay_adjustment = field_player->block_delay_adjustment;
4228 
4229 	  StorePlayer[jx][jy] = player->element_nr;
4230 
4231 	  player->jx = player->last_jx = jx;
4232 	  player->jy = player->last_jy = jy;
4233 
4234 	  break;
4235 	}
4236       }
4237     }
4238   }
4239 #endif
4240 
4241 #if 0
4242   printf("::: local_player->present == %d\n", local_player->present);
4243 #endif
4244 
4245   if (tape.playing)
4246   {
4247     /* when playing a tape, eliminate all players who do not participate */
4248 
4249 #if USE_NEW_PLAYER_ASSIGNMENTS
4250     for (i = 0; i < MAX_PLAYERS; i++)
4251     {
4252       if (stored_player[i].active &&
4253 	  !tape.player_participates[map_player_action[i]])
4254       {
4255 	struct PlayerInfo *player = &stored_player[i];
4256 	int jx = player->jx, jy = player->jy;
4257 
4258 	player->active = FALSE;
4259 	StorePlayer[jx][jy] = 0;
4260 	Feld[jx][jy] = EL_EMPTY;
4261       }
4262     }
4263 #else
4264     for (i = 0; i < MAX_PLAYERS; i++)
4265     {
4266       if (stored_player[i].active &&
4267 	  !tape.player_participates[i])
4268       {
4269 	struct PlayerInfo *player = &stored_player[i];
4270 	int jx = player->jx, jy = player->jy;
4271 
4272 	player->active = FALSE;
4273 	StorePlayer[jx][jy] = 0;
4274 	Feld[jx][jy] = EL_EMPTY;
4275       }
4276     }
4277 #endif
4278   }
4279   else if (!options.network && !setup.team_mode)	/* && !tape.playing */
4280   {
4281     /* when in single player mode, eliminate all but the first active player */
4282 
4283     for (i = 0; i < MAX_PLAYERS; i++)
4284     {
4285       if (stored_player[i].active)
4286       {
4287 	for (j = i + 1; j < MAX_PLAYERS; j++)
4288 	{
4289 	  if (stored_player[j].active)
4290 	  {
4291 	    struct PlayerInfo *player = &stored_player[j];
4292 	    int jx = player->jx, jy = player->jy;
4293 
4294 	    player->active = FALSE;
4295 	    player->present = FALSE;
4296 
4297 	    StorePlayer[jx][jy] = 0;
4298 	    Feld[jx][jy] = EL_EMPTY;
4299 	  }
4300 	}
4301       }
4302     }
4303   }
4304 
4305   /* when recording the game, store which players take part in the game */
4306   if (tape.recording)
4307   {
4308 #if USE_NEW_PLAYER_ASSIGNMENTS
4309     for (i = 0; i < MAX_PLAYERS; i++)
4310       if (stored_player[i].connected)
4311 	tape.player_participates[i] = TRUE;
4312 #else
4313     for (i = 0; i < MAX_PLAYERS; i++)
4314       if (stored_player[i].active)
4315 	tape.player_participates[i] = TRUE;
4316 #endif
4317   }
4318 
4319   if (options.debug)
4320   {
4321     for (i = 0; i < MAX_PLAYERS; i++)
4322     {
4323       struct PlayerInfo *player = &stored_player[i];
4324 
4325       printf("Player %d: present == %d, connected == %d, active == %d.\n",
4326 	     i+1,
4327 	     player->present,
4328 	     player->connected,
4329 	     player->active);
4330       if (local_player == player)
4331 	printf("Player 	%d is local player.\n", i+1);
4332     }
4333   }
4334 
4335   if (BorderElement == EL_EMPTY)
4336   {
4337     SBX_Left = 0;
4338     SBX_Right = lev_fieldx - SCR_FIELDX;
4339     SBY_Upper = 0;
4340     SBY_Lower = lev_fieldy - SCR_FIELDY;
4341   }
4342   else
4343   {
4344     SBX_Left = -1;
4345     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4346     SBY_Upper = -1;
4347     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4348   }
4349 
4350   if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4351     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4352 
4353   if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4354     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4355 
4356   /* if local player not found, look for custom element that might create
4357      the player (make some assumptions about the right custom element) */
4358   if (!local_player->present)
4359   {
4360     int start_x = 0, start_y = 0;
4361     int found_rating = 0;
4362     int found_element = EL_UNDEFINED;
4363     int player_nr = local_player->index_nr;
4364 
4365     SCAN_PLAYFIELD(x, y)
4366     {
4367       int element = Feld[x][y];
4368       int content;
4369       int xx, yy;
4370       boolean is_player;
4371 
4372       if (level.use_start_element[player_nr] &&
4373 	  level.start_element[player_nr] == element &&
4374 	  found_rating < 4)
4375       {
4376 	start_x = x;
4377 	start_y = y;
4378 
4379 	found_rating = 4;
4380 	found_element = element;
4381       }
4382 
4383       if (!IS_CUSTOM_ELEMENT(element))
4384 	continue;
4385 
4386       if (CAN_CHANGE(element))
4387       {
4388 	for (i = 0; i < element_info[element].num_change_pages; i++)
4389 	{
4390 	  /* check for player created from custom element as single target */
4391 	  content = element_info[element].change_page[i].target_element;
4392 	  is_player = ELEM_IS_PLAYER(content);
4393 
4394 	  if (is_player && (found_rating < 3 ||
4395 			    (found_rating == 3 && element < found_element)))
4396 	  {
4397 	    start_x = x;
4398 	    start_y = y;
4399 
4400 	    found_rating = 3;
4401 	    found_element = element;
4402 	  }
4403 	}
4404       }
4405 
4406       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4407       {
4408 	/* check for player created from custom element as explosion content */
4409 	content = element_info[element].content.e[xx][yy];
4410 	is_player = ELEM_IS_PLAYER(content);
4411 
4412 	if (is_player && (found_rating < 2 ||
4413 			  (found_rating == 2 && element < found_element)))
4414 	{
4415 	  start_x = x + xx - 1;
4416 	  start_y = y + yy - 1;
4417 
4418 	  found_rating = 2;
4419 	  found_element = element;
4420 	}
4421 
4422 	if (!CAN_CHANGE(element))
4423 	  continue;
4424 
4425 	for (i = 0; i < element_info[element].num_change_pages; i++)
4426 	{
4427 	  /* check for player created from custom element as extended target */
4428 	  content =
4429 	    element_info[element].change_page[i].target_content.e[xx][yy];
4430 
4431 	  is_player = ELEM_IS_PLAYER(content);
4432 
4433 	  if (is_player && (found_rating < 1 ||
4434 			    (found_rating == 1 && element < found_element)))
4435 	  {
4436 	    start_x = x + xx - 1;
4437 	    start_y = y + yy - 1;
4438 
4439 	    found_rating = 1;
4440 	    found_element = element;
4441 	  }
4442 	}
4443       }
4444     }
4445 
4446     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
4447 		start_x > SBX_Right + MIDPOSX ? SBX_Right :
4448 		start_x - MIDPOSX);
4449 
4450     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4451 		start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4452 		start_y - MIDPOSY);
4453   }
4454   else
4455   {
4456     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
4457 		local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4458 		local_player->jx - MIDPOSX);
4459 
4460     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4461 		local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4462 		local_player->jy - MIDPOSY);
4463   }
4464 
4465 #if 0
4466   /* do not use PLAYING mask for fading out from main screen */
4467   game_status = GAME_MODE_MAIN;
4468 #endif
4469 
4470   StopAnimation();
4471 
4472   if (!game.restart_level)
4473     CloseDoor(DOOR_CLOSE_1);
4474 
4475 #if 1
4476   if (level_editor_test_game)
4477     FadeSkipNextFadeIn();
4478   else
4479     FadeSetEnterScreen();
4480 #else
4481   if (level_editor_test_game)
4482     fading = fading_none;
4483   else
4484     fading = menu.destination;
4485 #endif
4486 
4487 #if 1
4488   FadeOut(REDRAW_FIELD);
4489 #else
4490   if (do_fading)
4491     FadeOut(REDRAW_FIELD);
4492 #endif
4493 
4494 #if 0
4495   game_status = GAME_MODE_PLAYING;
4496 #endif
4497 
4498   /* !!! FIX THIS (START) !!! */
4499   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4500   {
4501     InitGameEngine_EM();
4502 
4503     /* blit playfield from scroll buffer to normal back buffer for fading in */
4504     BlitScreenToBitmap_EM(backbuffer);
4505   }
4506   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4507   {
4508     InitGameEngine_SP();
4509 
4510     /* blit playfield from scroll buffer to normal back buffer for fading in */
4511     BlitScreenToBitmap_SP(backbuffer);
4512   }
4513   else
4514   {
4515     DrawLevel();
4516     DrawAllPlayers();
4517 
4518     /* after drawing the level, correct some elements */
4519     if (game.timegate_time_left == 0)
4520       CloseAllOpenTimegates();
4521 
4522     /* blit playfield from scroll buffer to normal back buffer for fading in */
4523     if (setup.soft_scrolling)
4524       BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4525 
4526     redraw_mask |= REDRAW_FROM_BACKBUFFER;
4527   }
4528   /* !!! FIX THIS (END) !!! */
4529 
4530 #if 1
4531   FadeIn(REDRAW_FIELD);
4532 #else
4533   if (do_fading)
4534     FadeIn(REDRAW_FIELD);
4535 
4536   BackToFront();
4537 #endif
4538 
4539   if (!game.restart_level)
4540   {
4541     /* copy default game door content to main double buffer */
4542     BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4543 	       DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4544   }
4545 
4546   SetPanelBackground();
4547   SetDrawBackgroundMask(REDRAW_DOOR_1);
4548 
4549 #if 1
4550   UpdateAndDisplayGameControlValues();
4551 #else
4552   UpdateGameDoorValues();
4553   DrawGameDoorValues();
4554 #endif
4555 
4556   if (!game.restart_level)
4557   {
4558     UnmapGameButtons();
4559     UnmapTapeButtons();
4560     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4561     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4562     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4563     MapGameButtons();
4564     MapTapeButtons();
4565 
4566     /* copy actual game door content to door double buffer for OpenDoor() */
4567     BlitBitmap(drawto, bitmap_db_door,
4568 	       DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4569 
4570     OpenDoor(DOOR_OPEN_ALL);
4571 
4572     PlaySound(SND_GAME_STARTING);
4573 
4574     if (setup.sound_music)
4575       PlayLevelMusic();
4576 
4577     KeyboardAutoRepeatOffUnlessAutoplay();
4578 
4579     if (options.debug)
4580     {
4581       for (i = 0; i < MAX_PLAYERS; i++)
4582 	printf("Player %d %sactive.\n",
4583 	       i + 1, (stored_player[i].active ? "" : "not "));
4584     }
4585   }
4586 
4587 #if 1
4588   UnmapAllGadgets();
4589 
4590   MapGameButtons();
4591   MapTapeButtons();
4592 #endif
4593 
4594   game.restart_level = FALSE;
4595 }
4596 
UpdateEngineValues(int actual_scroll_x,int actual_scroll_y)4597 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4598 {
4599   /* this is used for non-R'n'D game engines to update certain engine values */
4600 
4601   /* needed to determine if sounds are played within the visible screen area */
4602   scroll_x = actual_scroll_x;
4603   scroll_y = actual_scroll_y;
4604 }
4605 
InitMovDir(int x,int y)4606 void InitMovDir(int x, int y)
4607 {
4608   int i, element = Feld[x][y];
4609   static int xy[4][2] =
4610   {
4611     {  0, +1 },
4612     { +1,  0 },
4613     {  0, -1 },
4614     { -1,  0 }
4615   };
4616   static int direction[3][4] =
4617   {
4618     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4619     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4620     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4621   };
4622 
4623   switch (element)
4624   {
4625     case EL_BUG_RIGHT:
4626     case EL_BUG_UP:
4627     case EL_BUG_LEFT:
4628     case EL_BUG_DOWN:
4629       Feld[x][y] = EL_BUG;
4630       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4631       break;
4632 
4633     case EL_SPACESHIP_RIGHT:
4634     case EL_SPACESHIP_UP:
4635     case EL_SPACESHIP_LEFT:
4636     case EL_SPACESHIP_DOWN:
4637       Feld[x][y] = EL_SPACESHIP;
4638       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4639       break;
4640 
4641     case EL_BD_BUTTERFLY_RIGHT:
4642     case EL_BD_BUTTERFLY_UP:
4643     case EL_BD_BUTTERFLY_LEFT:
4644     case EL_BD_BUTTERFLY_DOWN:
4645       Feld[x][y] = EL_BD_BUTTERFLY;
4646       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4647       break;
4648 
4649     case EL_BD_FIREFLY_RIGHT:
4650     case EL_BD_FIREFLY_UP:
4651     case EL_BD_FIREFLY_LEFT:
4652     case EL_BD_FIREFLY_DOWN:
4653       Feld[x][y] = EL_BD_FIREFLY;
4654       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4655       break;
4656 
4657     case EL_PACMAN_RIGHT:
4658     case EL_PACMAN_UP:
4659     case EL_PACMAN_LEFT:
4660     case EL_PACMAN_DOWN:
4661       Feld[x][y] = EL_PACMAN;
4662       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4663       break;
4664 
4665     case EL_YAMYAM_LEFT:
4666     case EL_YAMYAM_RIGHT:
4667     case EL_YAMYAM_UP:
4668     case EL_YAMYAM_DOWN:
4669       Feld[x][y] = EL_YAMYAM;
4670       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4671       break;
4672 
4673     case EL_SP_SNIKSNAK:
4674       MovDir[x][y] = MV_UP;
4675       break;
4676 
4677     case EL_SP_ELECTRON:
4678       MovDir[x][y] = MV_LEFT;
4679       break;
4680 
4681     case EL_MOLE_LEFT:
4682     case EL_MOLE_RIGHT:
4683     case EL_MOLE_UP:
4684     case EL_MOLE_DOWN:
4685       Feld[x][y] = EL_MOLE;
4686       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4687       break;
4688 
4689     default:
4690       if (IS_CUSTOM_ELEMENT(element))
4691       {
4692 	struct ElementInfo *ei = &element_info[element];
4693 	int move_direction_initial = ei->move_direction_initial;
4694 	int move_pattern = ei->move_pattern;
4695 
4696 	if (move_direction_initial == MV_START_PREVIOUS)
4697 	{
4698 	  if (MovDir[x][y] != MV_NONE)
4699 	    return;
4700 
4701 	  move_direction_initial = MV_START_AUTOMATIC;
4702 	}
4703 
4704 	if (move_direction_initial == MV_START_RANDOM)
4705 	  MovDir[x][y] = 1 << RND(4);
4706 	else if (move_direction_initial & MV_ANY_DIRECTION)
4707 	  MovDir[x][y] = move_direction_initial;
4708 	else if (move_pattern == MV_ALL_DIRECTIONS ||
4709 		 move_pattern == MV_TURNING_LEFT ||
4710 		 move_pattern == MV_TURNING_RIGHT ||
4711 		 move_pattern == MV_TURNING_LEFT_RIGHT ||
4712 		 move_pattern == MV_TURNING_RIGHT_LEFT ||
4713 		 move_pattern == MV_TURNING_RANDOM)
4714 	  MovDir[x][y] = 1 << RND(4);
4715 	else if (move_pattern == MV_HORIZONTAL)
4716 	  MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4717 	else if (move_pattern == MV_VERTICAL)
4718 	  MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4719 	else if (move_pattern & MV_ANY_DIRECTION)
4720 	  MovDir[x][y] = element_info[element].move_pattern;
4721 	else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4722 		 move_pattern == MV_ALONG_RIGHT_SIDE)
4723 	{
4724 	  /* use random direction as default start direction */
4725 	  if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4726 	    MovDir[x][y] = 1 << RND(4);
4727 
4728 	  for (i = 0; i < NUM_DIRECTIONS; i++)
4729 	  {
4730 	    int x1 = x + xy[i][0];
4731 	    int y1 = y + xy[i][1];
4732 
4733 	    if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4734 	    {
4735 	      if (move_pattern == MV_ALONG_RIGHT_SIDE)
4736 		MovDir[x][y] = direction[0][i];
4737 	      else
4738 		MovDir[x][y] = direction[1][i];
4739 
4740 	      break;
4741 	    }
4742 	  }
4743 	}
4744       }
4745       else
4746       {
4747 	MovDir[x][y] = 1 << RND(4);
4748 
4749 	if (element != EL_BUG &&
4750 	    element != EL_SPACESHIP &&
4751 	    element != EL_BD_BUTTERFLY &&
4752 	    element != EL_BD_FIREFLY)
4753 	  break;
4754 
4755 	for (i = 0; i < NUM_DIRECTIONS; i++)
4756 	{
4757 	  int x1 = x + xy[i][0];
4758 	  int y1 = y + xy[i][1];
4759 
4760 	  if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4761 	  {
4762 	    if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4763 	    {
4764 	      MovDir[x][y] = direction[0][i];
4765 	      break;
4766 	    }
4767 	    else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4768 		     element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4769 	    {
4770 	      MovDir[x][y] = direction[1][i];
4771 	      break;
4772 	    }
4773 	  }
4774 	}
4775       }
4776       break;
4777   }
4778 
4779   GfxDir[x][y] = MovDir[x][y];
4780 }
4781 
InitAmoebaNr(int x,int y)4782 void InitAmoebaNr(int x, int y)
4783 {
4784   int i;
4785   int group_nr = AmoebeNachbarNr(x, y);
4786 
4787   if (group_nr == 0)
4788   {
4789     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4790     {
4791       if (AmoebaCnt[i] == 0)
4792       {
4793 	group_nr = i;
4794 	break;
4795       }
4796     }
4797   }
4798 
4799   AmoebaNr[x][y] = group_nr;
4800   AmoebaCnt[group_nr]++;
4801   AmoebaCnt2[group_nr]++;
4802 }
4803 
PlayerWins(struct PlayerInfo * player)4804 static void PlayerWins(struct PlayerInfo *player)
4805 {
4806   player->LevelSolved = TRUE;
4807   player->GameOver = TRUE;
4808 
4809   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4810 			 level.native_em_level->lev->score : player->score);
4811 
4812   player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4813   player->LevelSolved_CountingScore = player->score_final;
4814 }
4815 
GameWon()4816 void GameWon()
4817 {
4818   static int time, time_final;
4819   static int score, score_final;
4820   static int game_over_delay_1 = 0;
4821   static int game_over_delay_2 = 0;
4822   int game_over_delay_value_1 = 50;
4823   int game_over_delay_value_2 = 50;
4824 
4825   if (!local_player->LevelSolved_GameWon)
4826   {
4827     int i;
4828 
4829     /* do not start end game actions before the player stops moving (to exit) */
4830     if (local_player->MovPos)
4831       return;
4832 
4833     local_player->LevelSolved_GameWon = TRUE;
4834     local_player->LevelSolved_SaveTape = tape.recording;
4835     local_player->LevelSolved_SaveScore = !tape.playing;
4836 
4837     if (tape.auto_play)		/* tape might already be stopped here */
4838       tape.auto_play_level_solved = TRUE;
4839 
4840 #if 1
4841     TapeStop();
4842 #endif
4843 
4844     game_over_delay_1 = game_over_delay_value_1;
4845     game_over_delay_2 = game_over_delay_value_2;
4846 
4847     time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4848     score = score_final = local_player->score_final;
4849 
4850     if (TimeLeft > 0)
4851     {
4852       time_final = 0;
4853       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4854     }
4855     else if (level.time == 0 && TimePlayed < 999)
4856     {
4857       time_final = 999;
4858       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4859     }
4860 
4861     local_player->score_final = score_final;
4862 
4863     if (level_editor_test_game)
4864     {
4865       time = time_final;
4866       score = score_final;
4867 
4868 #if 1
4869       local_player->LevelSolved_CountingTime = time;
4870       local_player->LevelSolved_CountingScore = score;
4871 
4872       game_panel_controls[GAME_PANEL_TIME].value = time;
4873       game_panel_controls[GAME_PANEL_SCORE].value = score;
4874 
4875       DisplayGameControlValues();
4876 #else
4877       DrawGameValue_Time(time);
4878       DrawGameValue_Score(score);
4879 #endif
4880     }
4881 
4882     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4883     {
4884       if (ExitX >= 0 && ExitY >= 0)	/* local player has left the level */
4885       {
4886 	/* close exit door after last player */
4887 	if ((AllPlayersGone &&
4888 	     (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4889 	      Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4890 	      Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4891 	    Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4892 	    Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4893 	{
4894 	  int element = Feld[ExitX][ExitY];
4895 
4896 #if 0
4897 	  if (element == EL_EM_EXIT_OPEN ||
4898 	      element == EL_EM_STEEL_EXIT_OPEN)
4899 	  {
4900 	    Bang(ExitX, ExitY);
4901 	  }
4902 	  else
4903 #endif
4904 	  {
4905 	    Feld[ExitX][ExitY] =
4906 	      (element == EL_EXIT_OPEN		? EL_EXIT_CLOSING :
4907 	       element == EL_EM_EXIT_OPEN	? EL_EM_EXIT_CLOSING :
4908 	       element == EL_SP_EXIT_OPEN	? EL_SP_EXIT_CLOSING:
4909 	       element == EL_STEEL_EXIT_OPEN	? EL_STEEL_EXIT_CLOSING:
4910 	       EL_EM_STEEL_EXIT_CLOSING);
4911 
4912 	    PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4913 	  }
4914 	}
4915 
4916 	/* player disappears */
4917 	DrawLevelField(ExitX, ExitY);
4918       }
4919 
4920       for (i = 0; i < MAX_PLAYERS; i++)
4921       {
4922 	struct PlayerInfo *player = &stored_player[i];
4923 
4924 	if (player->present)
4925 	{
4926 	  RemovePlayer(player);
4927 
4928 	  /* player disappears */
4929 	  DrawLevelField(player->jx, player->jy);
4930 	}
4931       }
4932     }
4933 
4934     PlaySound(SND_GAME_WINNING);
4935   }
4936 
4937   if (game_over_delay_1 > 0)
4938   {
4939     game_over_delay_1--;
4940 
4941     return;
4942   }
4943 
4944   if (time != time_final)
4945   {
4946     int time_to_go = ABS(time_final - time);
4947     int time_count_dir = (time < time_final ? +1 : -1);
4948     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4949 
4950     time  += time_count_steps * time_count_dir;
4951     score += time_count_steps * level.score[SC_TIME_BONUS];
4952 
4953 #if 1
4954     local_player->LevelSolved_CountingTime = time;
4955     local_player->LevelSolved_CountingScore = score;
4956 
4957     game_panel_controls[GAME_PANEL_TIME].value = time;
4958     game_panel_controls[GAME_PANEL_SCORE].value = score;
4959 
4960     DisplayGameControlValues();
4961 #else
4962     DrawGameValue_Time(time);
4963     DrawGameValue_Score(score);
4964 #endif
4965 
4966     if (time == time_final)
4967       StopSound(SND_GAME_LEVELTIME_BONUS);
4968     else if (setup.sound_loops)
4969       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4970     else
4971       PlaySound(SND_GAME_LEVELTIME_BONUS);
4972 
4973     return;
4974   }
4975 
4976   local_player->LevelSolved_PanelOff = TRUE;
4977 
4978   if (game_over_delay_2 > 0)
4979   {
4980     game_over_delay_2--;
4981 
4982     return;
4983   }
4984 
4985 #if 1
4986   GameEnd();
4987 #endif
4988 }
4989 
GameEnd()4990 void GameEnd()
4991 {
4992   int hi_pos;
4993   boolean raise_level = FALSE;
4994 
4995   local_player->LevelSolved_GameEnd = TRUE;
4996 
4997   CloseDoor(DOOR_CLOSE_1);
4998 
4999   if (local_player->LevelSolved_SaveTape)
5000   {
5001 #if 0
5002     TapeStop();
5003 #endif
5004 
5005 #if 1
5006     SaveTapeChecked(tape.level_nr);	/* ask to save tape */
5007 #else
5008     SaveTape(tape.level_nr);		/* ask to save tape */
5009 #endif
5010   }
5011 
5012   if (level_editor_test_game)
5013   {
5014     game_status = GAME_MODE_MAIN;
5015 
5016 #if 1
5017     DrawAndFadeInMainMenu(REDRAW_FIELD);
5018 #else
5019     DrawMainMenu();
5020 #endif
5021 
5022     return;
5023   }
5024 
5025   if (!local_player->LevelSolved_SaveScore)
5026   {
5027 #if 1
5028     FadeOut(REDRAW_FIELD);
5029 #endif
5030 
5031     game_status = GAME_MODE_MAIN;
5032 
5033     DrawAndFadeInMainMenu(REDRAW_FIELD);
5034 
5035     return;
5036   }
5037 
5038   if (level_nr == leveldir_current->handicap_level)
5039   {
5040     leveldir_current->handicap_level++;
5041     SaveLevelSetup_SeriesInfo();
5042   }
5043 
5044   if (level_nr < leveldir_current->last_level)
5045     raise_level = TRUE;			/* advance to next level */
5046 
5047   if ((hi_pos = NewHiScore()) >= 0)
5048   {
5049     game_status = GAME_MODE_SCORES;
5050 
5051     DrawHallOfFame(hi_pos);
5052 
5053     if (raise_level)
5054     {
5055       level_nr++;
5056       TapeErase();
5057     }
5058   }
5059   else
5060   {
5061 #if 1
5062     FadeOut(REDRAW_FIELD);
5063 #endif
5064 
5065     game_status = GAME_MODE_MAIN;
5066 
5067     if (raise_level)
5068     {
5069       level_nr++;
5070       TapeErase();
5071     }
5072 
5073     DrawAndFadeInMainMenu(REDRAW_FIELD);
5074   }
5075 }
5076 
NewHiScore()5077 int NewHiScore()
5078 {
5079   int k, l;
5080   int position = -1;
5081 
5082   LoadScore(level_nr);
5083 
5084   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5085       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5086     return -1;
5087 
5088   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5089   {
5090     if (local_player->score_final > highscore[k].Score)
5091     {
5092       /* player has made it to the hall of fame */
5093 
5094       if (k < MAX_SCORE_ENTRIES - 1)
5095       {
5096 	int m = MAX_SCORE_ENTRIES - 1;
5097 
5098 #ifdef ONE_PER_NAME
5099 	for (l = k; l < MAX_SCORE_ENTRIES; l++)
5100 	  if (strEqual(setup.player_name, highscore[l].Name))
5101 	    m = l;
5102 	if (m == k)	/* player's new highscore overwrites his old one */
5103 	  goto put_into_list;
5104 #endif
5105 
5106 	for (l = m; l > k; l--)
5107 	{
5108 	  strcpy(highscore[l].Name, highscore[l - 1].Name);
5109 	  highscore[l].Score = highscore[l - 1].Score;
5110 	}
5111       }
5112 
5113 #ifdef ONE_PER_NAME
5114       put_into_list:
5115 #endif
5116       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5117       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5118       highscore[k].Score = local_player->score_final;
5119       position = k;
5120       break;
5121     }
5122 
5123 #ifdef ONE_PER_NAME
5124     else if (!strncmp(setup.player_name, highscore[k].Name,
5125 		      MAX_PLAYER_NAME_LEN))
5126       break;	/* player already there with a higher score */
5127 #endif
5128 
5129   }
5130 
5131   if (position >= 0)
5132     SaveScore(level_nr);
5133 
5134   return position;
5135 }
5136 
getElementMoveStepsizeExt(int x,int y,int direction)5137 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5138 {
5139   int element = Feld[x][y];
5140   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5141   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5142   int horiz_move = (dx != 0);
5143   int sign = (horiz_move ? dx : dy);
5144   int step = sign * element_info[element].move_stepsize;
5145 
5146   /* special values for move stepsize for spring and things on conveyor belt */
5147   if (horiz_move)
5148   {
5149     if (CAN_FALL(element) &&
5150 	y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5151       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5152     else if (element == EL_SPRING)
5153       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5154   }
5155 
5156   return step;
5157 }
5158 
getElementMoveStepsize(int x,int y)5159 inline static int getElementMoveStepsize(int x, int y)
5160 {
5161   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5162 }
5163 
InitPlayerGfxAnimation(struct PlayerInfo * player,int action,int dir)5164 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5165 {
5166   if (player->GfxAction != action || player->GfxDir != dir)
5167   {
5168 #if 0
5169     printf("Player frame reset! (%d => %d, %d => %d)\n",
5170 	   player->GfxAction, action, player->GfxDir, dir);
5171 #endif
5172 
5173     player->GfxAction = action;
5174     player->GfxDir = dir;
5175     player->Frame = 0;
5176     player->StepFrame = 0;
5177   }
5178 }
5179 
5180 #if USE_GFX_RESET_GFX_ANIMATION
ResetGfxFrame(int x,int y,boolean redraw)5181 static void ResetGfxFrame(int x, int y, boolean redraw)
5182 {
5183   int element = Feld[x][y];
5184   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5185   int last_gfx_frame = GfxFrame[x][y];
5186 
5187   if (graphic_info[graphic].anim_global_sync)
5188     GfxFrame[x][y] = FrameCounter;
5189   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5190     GfxFrame[x][y] = CustomValue[x][y];
5191   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5192     GfxFrame[x][y] = element_info[element].collect_score;
5193   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5194     GfxFrame[x][y] = ChangeDelay[x][y];
5195 
5196   if (redraw && GfxFrame[x][y] != last_gfx_frame)
5197     DrawLevelGraphicAnimation(x, y, graphic);
5198 }
5199 #endif
5200 
ResetGfxAnimation(int x,int y)5201 static void ResetGfxAnimation(int x, int y)
5202 {
5203   GfxAction[x][y] = ACTION_DEFAULT;
5204   GfxDir[x][y] = MovDir[x][y];
5205   GfxFrame[x][y] = 0;
5206 
5207 #if USE_GFX_RESET_GFX_ANIMATION
5208   ResetGfxFrame(x, y, FALSE);
5209 #endif
5210 }
5211 
ResetRandomAnimationValue(int x,int y)5212 static void ResetRandomAnimationValue(int x, int y)
5213 {
5214   GfxRandom[x][y] = INIT_GFX_RANDOM();
5215 }
5216 
InitMovingField(int x,int y,int direction)5217 void InitMovingField(int x, int y, int direction)
5218 {
5219   int element = Feld[x][y];
5220   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5221   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5222   int newx = x + dx;
5223   int newy = y + dy;
5224   boolean is_moving_before, is_moving_after;
5225 #if 0
5226   boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5227 #endif
5228 
5229   /* check if element was/is moving or being moved before/after mode change */
5230 #if 1
5231 #if 1
5232   is_moving_before = (WasJustMoving[x][y] != 0);
5233 #else
5234   /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5235   is_moving_before = WasJustMoving[x][y];
5236 #endif
5237 #else
5238   is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5239 #endif
5240   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5241 
5242   /* reset animation only for moving elements which change direction of moving
5243      or which just started or stopped moving
5244      (else CEs with property "can move" / "not moving" are reset each frame) */
5245 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5246 #if 1
5247   if (is_moving_before != is_moving_after ||
5248       direction != MovDir[x][y])
5249     ResetGfxAnimation(x, y);
5250 #else
5251   if ((is_moving_before || is_moving_after) && !continues_moving)
5252     ResetGfxAnimation(x, y);
5253 #endif
5254 #else
5255   if (!continues_moving)
5256     ResetGfxAnimation(x, y);
5257 #endif
5258 
5259   MovDir[x][y] = direction;
5260   GfxDir[x][y] = direction;
5261 
5262 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5263   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5264 		     direction == MV_DOWN && CAN_FALL(element) ?
5265 		     ACTION_FALLING : ACTION_MOVING);
5266 #else
5267   GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5268 		     ACTION_FALLING : ACTION_MOVING);
5269 #endif
5270 
5271   /* this is needed for CEs with property "can move" / "not moving" */
5272 
5273   if (is_moving_after)
5274   {
5275     if (Feld[newx][newy] == EL_EMPTY)
5276       Feld[newx][newy] = EL_BLOCKED;
5277 
5278     MovDir[newx][newy] = MovDir[x][y];
5279 
5280 #if USE_NEW_CUSTOM_VALUE
5281     CustomValue[newx][newy] = CustomValue[x][y];
5282 #endif
5283 
5284     GfxFrame[newx][newy] = GfxFrame[x][y];
5285     GfxRandom[newx][newy] = GfxRandom[x][y];
5286     GfxAction[newx][newy] = GfxAction[x][y];
5287     GfxDir[newx][newy] = GfxDir[x][y];
5288   }
5289 }
5290 
Moving2Blocked(int x,int y,int * goes_to_x,int * goes_to_y)5291 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5292 {
5293   int direction = MovDir[x][y];
5294   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5295   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5296 
5297   *goes_to_x = newx;
5298   *goes_to_y = newy;
5299 }
5300 
Blocked2Moving(int x,int y,int * comes_from_x,int * comes_from_y)5301 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5302 {
5303   int oldx = x, oldy = y;
5304   int direction = MovDir[x][y];
5305 
5306   if (direction == MV_LEFT)
5307     oldx++;
5308   else if (direction == MV_RIGHT)
5309     oldx--;
5310   else if (direction == MV_UP)
5311     oldy++;
5312   else if (direction == MV_DOWN)
5313     oldy--;
5314 
5315   *comes_from_x = oldx;
5316   *comes_from_y = oldy;
5317 }
5318 
MovingOrBlocked2Element(int x,int y)5319 int MovingOrBlocked2Element(int x, int y)
5320 {
5321   int element = Feld[x][y];
5322 
5323   if (element == EL_BLOCKED)
5324   {
5325     int oldx, oldy;
5326 
5327     Blocked2Moving(x, y, &oldx, &oldy);
5328     return Feld[oldx][oldy];
5329   }
5330   else
5331     return element;
5332 }
5333 
MovingOrBlocked2ElementIfNotLeaving(int x,int y)5334 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5335 {
5336   /* like MovingOrBlocked2Element(), but if element is moving
5337      and (x,y) is the field the moving element is just leaving,
5338      return EL_BLOCKED instead of the element value */
5339   int element = Feld[x][y];
5340 
5341   if (IS_MOVING(x, y))
5342   {
5343     if (element == EL_BLOCKED)
5344     {
5345       int oldx, oldy;
5346 
5347       Blocked2Moving(x, y, &oldx, &oldy);
5348       return Feld[oldx][oldy];
5349     }
5350     else
5351       return EL_BLOCKED;
5352   }
5353   else
5354     return element;
5355 }
5356 
RemoveField(int x,int y)5357 static void RemoveField(int x, int y)
5358 {
5359   Feld[x][y] = EL_EMPTY;
5360 
5361   MovPos[x][y] = 0;
5362   MovDir[x][y] = 0;
5363   MovDelay[x][y] = 0;
5364 
5365 #if USE_NEW_CUSTOM_VALUE
5366   CustomValue[x][y] = 0;
5367 #endif
5368 
5369   AmoebaNr[x][y] = 0;
5370   ChangeDelay[x][y] = 0;
5371   ChangePage[x][y] = -1;
5372   Pushed[x][y] = FALSE;
5373 
5374 #if 0
5375   ExplodeField[x][y] = EX_TYPE_NONE;
5376 #endif
5377 
5378   GfxElement[x][y] = EL_UNDEFINED;
5379   GfxAction[x][y] = ACTION_DEFAULT;
5380   GfxDir[x][y] = MV_NONE;
5381 #if 0
5382   /* !!! this would prevent the removed tile from being redrawn !!! */
5383   GfxRedraw[x][y] = GFX_REDRAW_NONE;
5384 #endif
5385 }
5386 
RemoveMovingField(int x,int y)5387 void RemoveMovingField(int x, int y)
5388 {
5389   int oldx = x, oldy = y, newx = x, newy = y;
5390   int element = Feld[x][y];
5391   int next_element = EL_UNDEFINED;
5392 
5393   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5394     return;
5395 
5396   if (IS_MOVING(x, y))
5397   {
5398     Moving2Blocked(x, y, &newx, &newy);
5399 
5400     if (Feld[newx][newy] != EL_BLOCKED)
5401     {
5402       /* element is moving, but target field is not free (blocked), but
5403 	 already occupied by something different (example: acid pool);
5404 	 in this case, only remove the moving field, but not the target */
5405 
5406       RemoveField(oldx, oldy);
5407 
5408       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5409 
5410       TEST_DrawLevelField(oldx, oldy);
5411 
5412       return;
5413     }
5414   }
5415   else if (element == EL_BLOCKED)
5416   {
5417     Blocked2Moving(x, y, &oldx, &oldy);
5418     if (!IS_MOVING(oldx, oldy))
5419       return;
5420   }
5421 
5422   if (element == EL_BLOCKED &&
5423       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5424        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5425        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5426        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5427        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5428        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5429     next_element = get_next_element(Feld[oldx][oldy]);
5430 
5431   RemoveField(oldx, oldy);
5432   RemoveField(newx, newy);
5433 
5434   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5435 
5436   if (next_element != EL_UNDEFINED)
5437     Feld[oldx][oldy] = next_element;
5438 
5439   TEST_DrawLevelField(oldx, oldy);
5440   TEST_DrawLevelField(newx, newy);
5441 }
5442 
DrawDynamite(int x,int y)5443 void DrawDynamite(int x, int y)
5444 {
5445   int sx = SCREENX(x), sy = SCREENY(y);
5446   int graphic = el2img(Feld[x][y]);
5447   int frame;
5448 
5449   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5450     return;
5451 
5452   if (IS_WALKABLE_INSIDE(Back[x][y]))
5453     return;
5454 
5455   if (Back[x][y])
5456     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5457   else if (Store[x][y])
5458     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5459 
5460   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5461 
5462   if (Back[x][y] || Store[x][y])
5463     DrawGraphicThruMask(sx, sy, graphic, frame);
5464   else
5465     DrawGraphic(sx, sy, graphic, frame);
5466 }
5467 
CheckDynamite(int x,int y)5468 void CheckDynamite(int x, int y)
5469 {
5470   if (MovDelay[x][y] != 0)	/* dynamite is still waiting to explode */
5471   {
5472     MovDelay[x][y]--;
5473 
5474     if (MovDelay[x][y] != 0)
5475     {
5476       DrawDynamite(x, y);
5477       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5478 
5479       return;
5480     }
5481   }
5482 
5483   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5484 
5485   Bang(x, y);
5486 }
5487 
setMinimalPlayerBoundaries(int * sx1,int * sy1,int * sx2,int * sy2)5488 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5489 {
5490   boolean num_checked_players = 0;
5491   int i;
5492 
5493   for (i = 0; i < MAX_PLAYERS; i++)
5494   {
5495     if (stored_player[i].active)
5496     {
5497       int sx = stored_player[i].jx;
5498       int sy = stored_player[i].jy;
5499 
5500       if (num_checked_players == 0)
5501       {
5502 	*sx1 = *sx2 = sx;
5503 	*sy1 = *sy2 = sy;
5504       }
5505       else
5506       {
5507 	*sx1 = MIN(*sx1, sx);
5508 	*sy1 = MIN(*sy1, sy);
5509 	*sx2 = MAX(*sx2, sx);
5510 	*sy2 = MAX(*sy2, sy);
5511       }
5512 
5513       num_checked_players++;
5514     }
5515   }
5516 }
5517 
checkIfAllPlayersFitToScreen_RND()5518 static boolean checkIfAllPlayersFitToScreen_RND()
5519 {
5520   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5521 
5522   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5523 
5524   return (sx2 - sx1 < SCR_FIELDX &&
5525 	  sy2 - sy1 < SCR_FIELDY);
5526 }
5527 
setScreenCenteredToAllPlayers(int * sx,int * sy)5528 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5529 {
5530   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5531 
5532   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5533 
5534   *sx = (sx1 + sx2) / 2;
5535   *sy = (sy1 + sy2) / 2;
5536 }
5537 
DrawRelocateScreen(int old_x,int old_y,int x,int y,int move_dir,boolean center_screen,boolean quick_relocation)5538 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5539 			boolean center_screen, boolean quick_relocation)
5540 {
5541   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5542   boolean no_delay = (tape.warp_forward);
5543   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5544   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5545 
5546   if (quick_relocation)
5547   {
5548     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5549     {
5550       if (!level.shifted_relocation || center_screen)
5551       {
5552 	/* quick relocation (without scrolling), with centering of screen */
5553 
5554 	scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5555 		    x > SBX_Right + MIDPOSX ? SBX_Right :
5556 		    x - MIDPOSX);
5557 
5558 	scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5559 		    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5560 		    y - MIDPOSY);
5561       }
5562       else
5563       {
5564 	/* quick relocation (without scrolling), but do not center screen */
5565 
5566 	int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5567 			       old_x > SBX_Right + MIDPOSX ? SBX_Right :
5568 			       old_x - MIDPOSX);
5569 
5570 	int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5571 			       old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5572 			       old_y - MIDPOSY);
5573 
5574 	int offset_x = x + (scroll_x - center_scroll_x);
5575 	int offset_y = y + (scroll_y - center_scroll_y);
5576 
5577 	scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5578 		    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5579 		    offset_x - MIDPOSX);
5580 
5581 	scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5582 		    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5583 		    offset_y - MIDPOSY);
5584       }
5585     }
5586     else
5587     {
5588 #if 1
5589       if (!level.shifted_relocation || center_screen)
5590       {
5591 	/* quick relocation (without scrolling), with centering of screen */
5592 
5593 	scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5594 		    x > SBX_Right + MIDPOSX ? SBX_Right :
5595 		    x - MIDPOSX);
5596 
5597 	scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5598 		    y > SBY_Lower + MIDPOSY ? SBY_Lower :
5599 		    y - MIDPOSY);
5600       }
5601       else
5602       {
5603 	/* quick relocation (without scrolling), but do not center screen */
5604 
5605 	int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5606 			       old_x > SBX_Right + MIDPOSX ? SBX_Right :
5607 			       old_x - MIDPOSX);
5608 
5609 	int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5610 			       old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5611 			       old_y - MIDPOSY);
5612 
5613 	int offset_x = x + (scroll_x - center_scroll_x);
5614 	int offset_y = y + (scroll_y - center_scroll_y);
5615 
5616 	scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5617 		    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5618 		    offset_x - MIDPOSX);
5619 
5620 	scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621 		    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5622 		    offset_y - MIDPOSY);
5623       }
5624 #else
5625       /* quick relocation (without scrolling), inside visible screen area */
5626 
5627       int offset = game.scroll_delay_value;
5628 
5629       if ((move_dir == MV_LEFT  && scroll_x > x - MIDPOSX + offset) ||
5630 	  (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5631 	scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5632 
5633       if ((move_dir == MV_UP   && scroll_y > y - MIDPOSY + offset) ||
5634 	  (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5635 	scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5636 
5637       /* don't scroll over playfield boundaries */
5638       if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5639 	scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5640 
5641       /* don't scroll over playfield boundaries */
5642       if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5643 	scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5644 #endif
5645     }
5646 
5647     RedrawPlayfield(TRUE, 0,0,0,0);
5648   }
5649   else
5650   {
5651 #if 1
5652     int scroll_xx, scroll_yy;
5653 
5654     if (!level.shifted_relocation || center_screen)
5655     {
5656       /* visible relocation (with scrolling), with centering of screen */
5657 
5658       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5659 		   x > SBX_Right + MIDPOSX ? SBX_Right :
5660 		   x - MIDPOSX);
5661 
5662       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5663 		   y > SBY_Lower + MIDPOSY ? SBY_Lower :
5664 		   y - MIDPOSY);
5665     }
5666     else
5667     {
5668       /* visible relocation (with scrolling), but do not center screen */
5669 
5670       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5671 			     old_x > SBX_Right + MIDPOSX ? SBX_Right :
5672 			     old_x - MIDPOSX);
5673 
5674       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5675 			     old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5676 			     old_y - MIDPOSY);
5677 
5678       int offset_x = x + (scroll_x - center_scroll_x);
5679       int offset_y = y + (scroll_y - center_scroll_y);
5680 
5681       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5682 		   offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5683 		   offset_x - MIDPOSX);
5684 
5685       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5686 		   offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5687 		   offset_y - MIDPOSY);
5688     }
5689 
5690 #else
5691 
5692     /* visible relocation (with scrolling), with centering of screen */
5693 
5694     int scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
5695 		     x > SBX_Right + MIDPOSX ? SBX_Right :
5696 		     x - MIDPOSX);
5697 
5698     int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5699 		     y > SBY_Lower + MIDPOSY ? SBY_Lower :
5700 		     y - MIDPOSY);
5701 #endif
5702 
5703     ScrollScreen(NULL, SCROLL_GO_ON);	/* scroll last frame to full tile */
5704 
5705     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5706     {
5707       int dx = 0, dy = 0;
5708       int fx = FX, fy = FY;
5709 
5710       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5711       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5712 
5713       if (dx == 0 && dy == 0)		/* no scrolling needed at all */
5714 	break;
5715 
5716       scroll_x -= dx;
5717       scroll_y -= dy;
5718 
5719       fx += dx * TILEX / 2;
5720       fy += dy * TILEY / 2;
5721 
5722       ScrollLevel(dx, dy);
5723       DrawAllPlayers();
5724 
5725       /* scroll in two steps of half tile size to make things smoother */
5726       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5727       FlushDisplay();
5728       Delay(wait_delay_value);
5729 
5730       /* scroll second step to align at full tile size */
5731       BackToFront();
5732       Delay(wait_delay_value);
5733     }
5734 
5735     DrawAllPlayers();
5736     BackToFront();
5737     Delay(wait_delay_value);
5738   }
5739 }
5740 
RelocatePlayer(int jx,int jy,int el_player_raw)5741 void RelocatePlayer(int jx, int jy, int el_player_raw)
5742 {
5743   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5744   int player_nr = GET_PLAYER_NR(el_player);
5745   struct PlayerInfo *player = &stored_player[player_nr];
5746   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5747   boolean no_delay = (tape.warp_forward);
5748   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5749   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5750   int old_jx = player->jx;
5751   int old_jy = player->jy;
5752   int old_element = Feld[old_jx][old_jy];
5753   int element = Feld[jx][jy];
5754   boolean player_relocated = (old_jx != jx || old_jy != jy);
5755 
5756   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5757   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5758   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5759   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5760   int leave_side_horiz = move_dir_horiz;
5761   int leave_side_vert  = move_dir_vert;
5762   int enter_side = enter_side_horiz | enter_side_vert;
5763   int leave_side = leave_side_horiz | leave_side_vert;
5764 
5765   if (player->GameOver)		/* do not reanimate dead player */
5766     return;
5767 
5768   if (!player_relocated)	/* no need to relocate the player */
5769     return;
5770 
5771   if (IS_PLAYER(jx, jy))	/* player already placed at new position */
5772   {
5773     RemoveField(jx, jy);	/* temporarily remove newly placed player */
5774     DrawLevelField(jx, jy);
5775   }
5776 
5777   if (player->present)
5778   {
5779     while (player->MovPos)
5780     {
5781       ScrollPlayer(player, SCROLL_GO_ON);
5782       ScrollScreen(NULL, SCROLL_GO_ON);
5783 
5784       AdvanceFrameAndPlayerCounters(player->index_nr);
5785 
5786       DrawPlayer(player);
5787 
5788       BackToFront();
5789       Delay(wait_delay_value);
5790     }
5791 
5792     DrawPlayer(player);		/* needed here only to cleanup last field */
5793     DrawLevelField(player->jx, player->jy);	/* remove player graphic */
5794 
5795     player->is_moving = FALSE;
5796   }
5797 
5798   if (IS_CUSTOM_ELEMENT(old_element))
5799     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5800 			       CE_LEFT_BY_PLAYER,
5801 			       player->index_bit, leave_side);
5802 
5803   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5804 				      CE_PLAYER_LEAVES_X,
5805 				      player->index_bit, leave_side);
5806 
5807   Feld[jx][jy] = el_player;
5808   InitPlayerField(jx, jy, el_player, TRUE);
5809 
5810   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5811      possible that the relocation target field did not contain a player element,
5812      but a walkable element, to which the new player was relocated -- in this
5813      case, restore that (already initialized!) element on the player field */
5814   if (!ELEM_IS_PLAYER(element))	/* player may be set on walkable element */
5815   {
5816     Feld[jx][jy] = element;	/* restore previously existing element */
5817 #if 0
5818     /* !!! do not initialize already initialized element a second time !!! */
5819     /* (this causes at least problems with "element creation" CE trigger for
5820        already existing elements, and existing Sokoban fields counted twice) */
5821     InitField(jx, jy, FALSE);
5822 #endif
5823   }
5824 
5825   /* only visually relocate centered player */
5826   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5827 		     FALSE, level.instant_relocation);
5828 
5829   TestIfPlayerTouchesBadThing(jx, jy);
5830   TestIfPlayerTouchesCustomElement(jx, jy);
5831 
5832   if (IS_CUSTOM_ELEMENT(element))
5833     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5834 			       player->index_bit, enter_side);
5835 
5836   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5837 				      player->index_bit, enter_side);
5838 
5839 #if 1
5840   if (player->is_switching)
5841   {
5842     /* ensure that relocation while still switching an element does not cause
5843        a new element to be treated as also switched directly after relocation
5844        (this is important for teleporter switches that teleport the player to
5845        a place where another teleporter switch is in the same direction, which
5846        would then incorrectly be treated as immediately switched before the
5847        direction key that caused the switch was released) */
5848 
5849     player->switch_x += jx - old_jx;
5850     player->switch_y += jy - old_jy;
5851   }
5852 #endif
5853 }
5854 
Explode(int ex,int ey,int phase,int mode)5855 void Explode(int ex, int ey, int phase, int mode)
5856 {
5857   int x, y;
5858   int last_phase;
5859   int border_element;
5860 
5861   /* !!! eliminate this variable !!! */
5862   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5863 
5864   if (game.explosions_delayed)
5865   {
5866     ExplodeField[ex][ey] = mode;
5867     return;
5868   }
5869 
5870   if (phase == EX_PHASE_START)		/* initialize 'Store[][]' field */
5871   {
5872     int center_element = Feld[ex][ey];
5873     int artwork_element, explosion_element;	/* set these values later */
5874 
5875 #if 0
5876     /* --- This is only really needed (and now handled) in "Impact()". --- */
5877     /* do not explode moving elements that left the explode field in time */
5878     if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5879 	center_element == EL_EMPTY &&
5880 	(mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5881       return;
5882 #endif
5883 
5884 #if 0
5885     /* !!! at this place, the center element may be EL_BLOCKED !!! */
5886     if (mode == EX_TYPE_NORMAL ||
5887 	mode == EX_TYPE_CENTER ||
5888 	mode == EX_TYPE_CROSS)
5889       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5890 #endif
5891 
5892     /* remove things displayed in background while burning dynamite */
5893     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5894       Back[ex][ey] = 0;
5895 
5896     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5897     {
5898       /* put moving element to center field (and let it explode there) */
5899       center_element = MovingOrBlocked2Element(ex, ey);
5900       RemoveMovingField(ex, ey);
5901       Feld[ex][ey] = center_element;
5902     }
5903 
5904     /* now "center_element" is finally determined -- set related values now */
5905     artwork_element = center_element;		/* for custom player artwork */
5906     explosion_element = center_element;		/* for custom player artwork */
5907 
5908     if (IS_PLAYER(ex, ey))
5909     {
5910       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5911 
5912       artwork_element = stored_player[player_nr].artwork_element;
5913 
5914       if (level.use_explosion_element[player_nr])
5915       {
5916 	explosion_element = level.explosion_element[player_nr];
5917 	artwork_element = explosion_element;
5918       }
5919     }
5920 
5921 #if 1
5922     if (mode == EX_TYPE_NORMAL ||
5923 	mode == EX_TYPE_CENTER ||
5924 	mode == EX_TYPE_CROSS)
5925       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5926 #endif
5927 
5928     last_phase = element_info[explosion_element].explosion_delay + 1;
5929 
5930     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5931     {
5932       int xx = x - ex + 1;
5933       int yy = y - ey + 1;
5934       int element;
5935 
5936       if (!IN_LEV_FIELD(x, y) ||
5937 	  (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5938 	  (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5939 	continue;
5940 
5941       element = Feld[x][y];
5942 
5943       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5944       {
5945 	element = MovingOrBlocked2Element(x, y);
5946 
5947 	if (!IS_EXPLOSION_PROOF(element))
5948 	  RemoveMovingField(x, y);
5949       }
5950 
5951       /* indestructible elements can only explode in center (but not flames) */
5952       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5953 					   mode == EX_TYPE_BORDER)) ||
5954 	  element == EL_FLAMES)
5955 	continue;
5956 
5957       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5958 	 behaviour, for example when touching a yamyam that explodes to rocks
5959 	 with active deadly shield, a rock is created under the player !!! */
5960       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5961 #if 0
5962       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5963 	  (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5964 	   (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5965 #else
5966       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5967 #endif
5968       {
5969 	if (IS_ACTIVE_BOMB(element))
5970 	{
5971 	  /* re-activate things under the bomb like gate or penguin */
5972 	  Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5973 	  Back[x][y] = 0;
5974 	}
5975 
5976 	continue;
5977       }
5978 
5979       /* save walkable background elements while explosion on same tile */
5980       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5981 	  (x != ex || y != ey || mode == EX_TYPE_BORDER))
5982 	Back[x][y] = element;
5983 
5984       /* ignite explodable elements reached by other explosion */
5985       if (element == EL_EXPLOSION)
5986 	element = Store2[x][y];
5987 
5988       if (AmoebaNr[x][y] &&
5989 	  (element == EL_AMOEBA_FULL ||
5990 	   element == EL_BD_AMOEBA ||
5991 	   element == EL_AMOEBA_GROWING))
5992       {
5993 	AmoebaCnt[AmoebaNr[x][y]]--;
5994 	AmoebaCnt2[AmoebaNr[x][y]]--;
5995       }
5996 
5997       RemoveField(x, y);
5998 
5999       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6000       {
6001 	int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6002 
6003 	Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6004 
6005 	if (PLAYERINFO(ex, ey)->use_murphy)
6006 	  Store[x][y] = EL_EMPTY;
6007       }
6008 
6009       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
6010 	 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
6011       else if (ELEM_IS_PLAYER(center_element))
6012 	Store[x][y] = EL_EMPTY;
6013       else if (center_element == EL_YAMYAM)
6014 	Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6015       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6016 	Store[x][y] = element_info[center_element].content.e[xx][yy];
6017 #if 1
6018       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6019 	 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6020 	 otherwise) -- FIX THIS !!! */
6021       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6022 	Store[x][y] = element_info[element].content.e[1][1];
6023 #else
6024       else if (!CAN_EXPLODE(element))
6025 	Store[x][y] = element_info[element].content.e[1][1];
6026 #endif
6027       else
6028 	Store[x][y] = EL_EMPTY;
6029 
6030       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6031 	  center_element == EL_AMOEBA_TO_DIAMOND)
6032 	Store2[x][y] = element;
6033 
6034       Feld[x][y] = EL_EXPLOSION;
6035       GfxElement[x][y] = artwork_element;
6036 
6037       ExplodePhase[x][y] = 1;
6038       ExplodeDelay[x][y] = last_phase;
6039 
6040       Stop[x][y] = TRUE;
6041     }
6042 
6043     if (center_element == EL_YAMYAM)
6044       game.yamyam_content_nr =
6045 	(game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6046 
6047     return;
6048   }
6049 
6050   if (Stop[ex][ey])
6051     return;
6052 
6053   x = ex;
6054   y = ey;
6055 
6056   if (phase == 1)
6057     GfxFrame[x][y] = 0;		/* restart explosion animation */
6058 
6059   last_phase = ExplodeDelay[x][y];
6060 
6061   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6062 
6063 #ifdef DEBUG
6064 
6065   /* activate this even in non-DEBUG version until cause for crash in
6066      getGraphicAnimationFrame() (see below) is found and eliminated */
6067 
6068 #endif
6069 #if 1
6070 
6071 #if 1
6072   /* this can happen if the player leaves an explosion just in time */
6073   if (GfxElement[x][y] == EL_UNDEFINED)
6074     GfxElement[x][y] = EL_EMPTY;
6075 #else
6076   if (GfxElement[x][y] == EL_UNDEFINED)
6077   {
6078     printf("\n\n");
6079     printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6080     printf("Explode(): This should never happen!\n");
6081     printf("\n\n");
6082 
6083     GfxElement[x][y] = EL_EMPTY;
6084   }
6085 #endif
6086 
6087 #endif
6088 
6089   border_element = Store2[x][y];
6090   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6091     border_element = StorePlayer[x][y];
6092 
6093   if (phase == element_info[border_element].ignition_delay ||
6094       phase == last_phase)
6095   {
6096     boolean border_explosion = FALSE;
6097 
6098     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6099 	!PLAYER_EXPLOSION_PROTECTED(x, y))
6100     {
6101       KillPlayerUnlessExplosionProtected(x, y);
6102       border_explosion = TRUE;
6103     }
6104     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6105     {
6106       Feld[x][y] = Store2[x][y];
6107       Store2[x][y] = 0;
6108       Bang(x, y);
6109       border_explosion = TRUE;
6110     }
6111     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6112     {
6113       AmoebeUmwandeln(x, y);
6114       Store2[x][y] = 0;
6115       border_explosion = TRUE;
6116     }
6117 
6118     /* if an element just explodes due to another explosion (chain-reaction),
6119        do not immediately end the new explosion when it was the last frame of
6120        the explosion (as it would be done in the following "if"-statement!) */
6121     if (border_explosion && phase == last_phase)
6122       return;
6123   }
6124 
6125   if (phase == last_phase)
6126   {
6127     int element;
6128 
6129     element = Feld[x][y] = Store[x][y];
6130     Store[x][y] = Store2[x][y] = 0;
6131     GfxElement[x][y] = EL_UNDEFINED;
6132 
6133     /* player can escape from explosions and might therefore be still alive */
6134     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6135 	element <= EL_PLAYER_IS_EXPLODING_4)
6136     {
6137       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6138       int explosion_element = EL_PLAYER_1 + player_nr;
6139       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6140       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6141 
6142       if (level.use_explosion_element[player_nr])
6143 	explosion_element = level.explosion_element[player_nr];
6144 
6145       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6146 		    element_info[explosion_element].content.e[xx][yy]);
6147     }
6148 
6149     /* restore probably existing indestructible background element */
6150     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6151       element = Feld[x][y] = Back[x][y];
6152     Back[x][y] = 0;
6153 
6154     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6155     GfxDir[x][y] = MV_NONE;
6156     ChangeDelay[x][y] = 0;
6157     ChangePage[x][y] = -1;
6158 
6159 #if USE_NEW_CUSTOM_VALUE
6160     CustomValue[x][y] = 0;
6161 #endif
6162 
6163     InitField_WithBug2(x, y, FALSE);
6164 
6165     TEST_DrawLevelField(x, y);
6166 
6167     TestIfElementTouchesCustomElement(x, y);
6168 
6169     if (GFX_CRUMBLED(element))
6170       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6171 
6172     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6173       StorePlayer[x][y] = 0;
6174 
6175     if (ELEM_IS_PLAYER(element))
6176       RelocatePlayer(x, y, element);
6177   }
6178   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6179   {
6180     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6181     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6182 
6183     if (phase == delay)
6184       TEST_DrawLevelFieldCrumbled(x, y);
6185 
6186     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6187     {
6188       DrawLevelElement(x, y, Back[x][y]);
6189       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6190     }
6191     else if (IS_WALKABLE_UNDER(Back[x][y]))
6192     {
6193       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6194       DrawLevelElementThruMask(x, y, Back[x][y]);
6195     }
6196     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6197       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6198   }
6199 }
6200 
DynaExplode(int ex,int ey)6201 void DynaExplode(int ex, int ey)
6202 {
6203   int i, j;
6204   int dynabomb_element = Feld[ex][ey];
6205   int dynabomb_size = 1;
6206   boolean dynabomb_xl = FALSE;
6207   struct PlayerInfo *player;
6208   static int xy[4][2] =
6209   {
6210     { 0, -1 },
6211     { -1, 0 },
6212     { +1, 0 },
6213     { 0, +1 }
6214   };
6215 
6216   if (IS_ACTIVE_BOMB(dynabomb_element))
6217   {
6218     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6219     dynabomb_size = player->dynabomb_size;
6220     dynabomb_xl = player->dynabomb_xl;
6221     player->dynabombs_left++;
6222   }
6223 
6224   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6225 
6226   for (i = 0; i < NUM_DIRECTIONS; i++)
6227   {
6228     for (j = 1; j <= dynabomb_size; j++)
6229     {
6230       int x = ex + j * xy[i][0];
6231       int y = ey + j * xy[i][1];
6232       int element;
6233 
6234       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6235 	break;
6236 
6237       element = Feld[x][y];
6238 
6239       /* do not restart explosions of fields with active bombs */
6240       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6241 	continue;
6242 
6243       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6244 
6245       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6246 	  !IS_DIGGABLE(element) && !dynabomb_xl)
6247 	break;
6248     }
6249   }
6250 }
6251 
Bang(int x,int y)6252 void Bang(int x, int y)
6253 {
6254   int element = MovingOrBlocked2Element(x, y);
6255   int explosion_type = EX_TYPE_NORMAL;
6256 
6257   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6258   {
6259     struct PlayerInfo *player = PLAYERINFO(x, y);
6260 
6261 #if USE_FIX_CE_ACTION_WITH_PLAYER
6262     element = Feld[x][y] = player->initial_element;
6263 #else
6264     element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6265 			    player->element_nr);
6266 #endif
6267 
6268     if (level.use_explosion_element[player->index_nr])
6269     {
6270       int explosion_element = level.explosion_element[player->index_nr];
6271 
6272       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6273 	explosion_type = EX_TYPE_CROSS;
6274       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6275 	explosion_type = EX_TYPE_CENTER;
6276     }
6277   }
6278 
6279   switch (element)
6280   {
6281     case EL_BUG:
6282     case EL_SPACESHIP:
6283     case EL_BD_BUTTERFLY:
6284     case EL_BD_FIREFLY:
6285     case EL_YAMYAM:
6286     case EL_DARK_YAMYAM:
6287     case EL_ROBOT:
6288     case EL_PACMAN:
6289     case EL_MOLE:
6290       RaiseScoreElement(element);
6291       break;
6292 
6293     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6294     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6295     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6296     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6297     case EL_DYNABOMB_INCREASE_NUMBER:
6298     case EL_DYNABOMB_INCREASE_SIZE:
6299     case EL_DYNABOMB_INCREASE_POWER:
6300       explosion_type = EX_TYPE_DYNA;
6301       break;
6302 
6303     case EL_DC_LANDMINE:
6304 #if 0
6305     case EL_EM_EXIT_OPEN:
6306     case EL_EM_STEEL_EXIT_OPEN:
6307 #endif
6308       explosion_type = EX_TYPE_CENTER;
6309       break;
6310 
6311     case EL_PENGUIN:
6312     case EL_LAMP:
6313     case EL_LAMP_ACTIVE:
6314     case EL_AMOEBA_TO_DIAMOND:
6315       if (!IS_PLAYER(x, y))	/* penguin and player may be at same field */
6316 	explosion_type = EX_TYPE_CENTER;
6317       break;
6318 
6319     default:
6320       if (element_info[element].explosion_type == EXPLODES_CROSS)
6321 	explosion_type = EX_TYPE_CROSS;
6322       else if (element_info[element].explosion_type == EXPLODES_1X1)
6323 	explosion_type = EX_TYPE_CENTER;
6324       break;
6325   }
6326 
6327   if (explosion_type == EX_TYPE_DYNA)
6328     DynaExplode(x, y);
6329   else
6330     Explode(x, y, EX_PHASE_START, explosion_type);
6331 
6332   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6333 }
6334 
SplashAcid(int x,int y)6335 void SplashAcid(int x, int y)
6336 {
6337   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6338       (!IN_LEV_FIELD(x - 1, y - 2) ||
6339        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6340     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6341 
6342   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6343       (!IN_LEV_FIELD(x + 1, y - 2) ||
6344        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6345     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6346 
6347   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6348 }
6349 
InitBeltMovement()6350 static void InitBeltMovement()
6351 {
6352   static int belt_base_element[4] =
6353   {
6354     EL_CONVEYOR_BELT_1_LEFT,
6355     EL_CONVEYOR_BELT_2_LEFT,
6356     EL_CONVEYOR_BELT_3_LEFT,
6357     EL_CONVEYOR_BELT_4_LEFT
6358   };
6359   static int belt_base_active_element[4] =
6360   {
6361     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6362     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6363     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6364     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6365   };
6366 
6367   int x, y, i, j;
6368 
6369   /* set frame order for belt animation graphic according to belt direction */
6370   for (i = 0; i < NUM_BELTS; i++)
6371   {
6372     int belt_nr = i;
6373 
6374     for (j = 0; j < NUM_BELT_PARTS; j++)
6375     {
6376       int element = belt_base_active_element[belt_nr] + j;
6377       int graphic_1 = el2img(element);
6378       int graphic_2 = el2panelimg(element);
6379 
6380       if (game.belt_dir[i] == MV_LEFT)
6381       {
6382 	graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6383 	graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6384       }
6385       else
6386       {
6387 	graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6388 	graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6389       }
6390     }
6391   }
6392 
6393   SCAN_PLAYFIELD(x, y)
6394   {
6395     int element = Feld[x][y];
6396 
6397     for (i = 0; i < NUM_BELTS; i++)
6398     {
6399       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6400       {
6401 	int e_belt_nr = getBeltNrFromBeltElement(element);
6402 	int belt_nr = i;
6403 
6404 	if (e_belt_nr == belt_nr)
6405 	{
6406 	  int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6407 
6408 	  Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6409 	}
6410       }
6411     }
6412   }
6413 }
6414 
ToggleBeltSwitch(int x,int y)6415 static void ToggleBeltSwitch(int x, int y)
6416 {
6417   static int belt_base_element[4] =
6418   {
6419     EL_CONVEYOR_BELT_1_LEFT,
6420     EL_CONVEYOR_BELT_2_LEFT,
6421     EL_CONVEYOR_BELT_3_LEFT,
6422     EL_CONVEYOR_BELT_4_LEFT
6423   };
6424   static int belt_base_active_element[4] =
6425   {
6426     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6427     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6428     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6429     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6430   };
6431   static int belt_base_switch_element[4] =
6432   {
6433     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6434     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6435     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6436     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6437   };
6438   static int belt_move_dir[4] =
6439   {
6440     MV_LEFT,
6441     MV_NONE,
6442     MV_RIGHT,
6443     MV_NONE,
6444   };
6445 
6446   int element = Feld[x][y];
6447   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6448   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6449   int belt_dir = belt_move_dir[belt_dir_nr];
6450   int xx, yy, i;
6451 
6452   if (!IS_BELT_SWITCH(element))
6453     return;
6454 
6455   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6456   game.belt_dir[belt_nr] = belt_dir;
6457 
6458   if (belt_dir_nr == 3)
6459     belt_dir_nr = 1;
6460 
6461   /* set frame order for belt animation graphic according to belt direction */
6462   for (i = 0; i < NUM_BELT_PARTS; i++)
6463   {
6464     int element = belt_base_active_element[belt_nr] + i;
6465     int graphic_1 = el2img(element);
6466     int graphic_2 = el2panelimg(element);
6467 
6468     if (belt_dir == MV_LEFT)
6469     {
6470       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6471       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6472     }
6473     else
6474     {
6475       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6476       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6477     }
6478   }
6479 
6480   SCAN_PLAYFIELD(xx, yy)
6481   {
6482     int element = Feld[xx][yy];
6483 
6484     if (IS_BELT_SWITCH(element))
6485     {
6486       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6487 
6488       if (e_belt_nr == belt_nr)
6489       {
6490 	Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6491 	TEST_DrawLevelField(xx, yy);
6492       }
6493     }
6494     else if (IS_BELT(element) && belt_dir != MV_NONE)
6495     {
6496       int e_belt_nr = getBeltNrFromBeltElement(element);
6497 
6498       if (e_belt_nr == belt_nr)
6499       {
6500 	int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6501 
6502 	Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6503 	TEST_DrawLevelField(xx, yy);
6504       }
6505     }
6506     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6507     {
6508       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6509 
6510       if (e_belt_nr == belt_nr)
6511       {
6512 	int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6513 
6514 	Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6515 	TEST_DrawLevelField(xx, yy);
6516       }
6517     }
6518   }
6519 }
6520 
ToggleSwitchgateSwitch(int x,int y)6521 static void ToggleSwitchgateSwitch(int x, int y)
6522 {
6523   int xx, yy;
6524 
6525   game.switchgate_pos = !game.switchgate_pos;
6526 
6527   SCAN_PLAYFIELD(xx, yy)
6528   {
6529     int element = Feld[xx][yy];
6530 
6531 #if !USE_BOTH_SWITCHGATE_SWITCHES
6532     if (element == EL_SWITCHGATE_SWITCH_UP ||
6533 	element == EL_SWITCHGATE_SWITCH_DOWN)
6534     {
6535       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6536       TEST_DrawLevelField(xx, yy);
6537     }
6538     else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6539 	     element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6540     {
6541       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6542       TEST_DrawLevelField(xx, yy);
6543     }
6544 #else
6545     if (element == EL_SWITCHGATE_SWITCH_UP)
6546     {
6547       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6548       TEST_DrawLevelField(xx, yy);
6549     }
6550     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6551     {
6552       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6553       TEST_DrawLevelField(xx, yy);
6554     }
6555     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6556     {
6557       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6558       TEST_DrawLevelField(xx, yy);
6559     }
6560     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6561     {
6562       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6563       TEST_DrawLevelField(xx, yy);
6564     }
6565 #endif
6566     else if (element == EL_SWITCHGATE_OPEN ||
6567 	     element == EL_SWITCHGATE_OPENING)
6568     {
6569       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6570 
6571       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6572     }
6573     else if (element == EL_SWITCHGATE_CLOSED ||
6574 	     element == EL_SWITCHGATE_CLOSING)
6575     {
6576       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6577 
6578       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6579     }
6580   }
6581 }
6582 
getInvisibleActiveFromInvisibleElement(int element)6583 static int getInvisibleActiveFromInvisibleElement(int element)
6584 {
6585   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6586 	  element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6587 	  element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6588 	  element);
6589 }
6590 
getInvisibleFromInvisibleActiveElement(int element)6591 static int getInvisibleFromInvisibleActiveElement(int element)
6592 {
6593   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6594 	  element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6595 	  element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6596 	  element);
6597 }
6598 
RedrawAllLightSwitchesAndInvisibleElements()6599 static void RedrawAllLightSwitchesAndInvisibleElements()
6600 {
6601   int x, y;
6602 
6603   SCAN_PLAYFIELD(x, y)
6604   {
6605     int element = Feld[x][y];
6606 
6607     if (element == EL_LIGHT_SWITCH &&
6608 	game.light_time_left > 0)
6609     {
6610       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6611       TEST_DrawLevelField(x, y);
6612     }
6613     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6614 	     game.light_time_left == 0)
6615     {
6616       Feld[x][y] = EL_LIGHT_SWITCH;
6617       TEST_DrawLevelField(x, y);
6618     }
6619     else if (element == EL_EMC_DRIPPER &&
6620 	     game.light_time_left > 0)
6621     {
6622       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6623       TEST_DrawLevelField(x, y);
6624     }
6625     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6626 	     game.light_time_left == 0)
6627     {
6628       Feld[x][y] = EL_EMC_DRIPPER;
6629       TEST_DrawLevelField(x, y);
6630     }
6631     else if (element == EL_INVISIBLE_STEELWALL ||
6632 	     element == EL_INVISIBLE_WALL ||
6633 	     element == EL_INVISIBLE_SAND)
6634     {
6635       if (game.light_time_left > 0)
6636 	Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6637 
6638       TEST_DrawLevelField(x, y);
6639 
6640       /* uncrumble neighbour fields, if needed */
6641       if (element == EL_INVISIBLE_SAND)
6642 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6643     }
6644     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6645 	     element == EL_INVISIBLE_WALL_ACTIVE ||
6646 	     element == EL_INVISIBLE_SAND_ACTIVE)
6647     {
6648       if (game.light_time_left == 0)
6649 	Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6650 
6651       TEST_DrawLevelField(x, y);
6652 
6653       /* re-crumble neighbour fields, if needed */
6654       if (element == EL_INVISIBLE_SAND)
6655 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6656     }
6657   }
6658 }
6659 
RedrawAllInvisibleElementsForLenses()6660 static void RedrawAllInvisibleElementsForLenses()
6661 {
6662   int x, y;
6663 
6664   SCAN_PLAYFIELD(x, y)
6665   {
6666     int element = Feld[x][y];
6667 
6668     if (element == EL_EMC_DRIPPER &&
6669 	game.lenses_time_left > 0)
6670     {
6671       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6672       TEST_DrawLevelField(x, y);
6673     }
6674     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6675 	     game.lenses_time_left == 0)
6676     {
6677       Feld[x][y] = EL_EMC_DRIPPER;
6678       TEST_DrawLevelField(x, y);
6679     }
6680     else if (element == EL_INVISIBLE_STEELWALL ||
6681 	     element == EL_INVISIBLE_WALL ||
6682 	     element == EL_INVISIBLE_SAND)
6683     {
6684       if (game.lenses_time_left > 0)
6685 	Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6686 
6687       TEST_DrawLevelField(x, y);
6688 
6689       /* uncrumble neighbour fields, if needed */
6690       if (element == EL_INVISIBLE_SAND)
6691 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6692     }
6693     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6694 	     element == EL_INVISIBLE_WALL_ACTIVE ||
6695 	     element == EL_INVISIBLE_SAND_ACTIVE)
6696     {
6697       if (game.lenses_time_left == 0)
6698 	Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6699 
6700       TEST_DrawLevelField(x, y);
6701 
6702       /* re-crumble neighbour fields, if needed */
6703       if (element == EL_INVISIBLE_SAND)
6704 	TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6705     }
6706   }
6707 }
6708 
RedrawAllInvisibleElementsForMagnifier()6709 static void RedrawAllInvisibleElementsForMagnifier()
6710 {
6711   int x, y;
6712 
6713   SCAN_PLAYFIELD(x, y)
6714   {
6715     int element = Feld[x][y];
6716 
6717     if (element == EL_EMC_FAKE_GRASS &&
6718 	game.magnify_time_left > 0)
6719     {
6720       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6721       TEST_DrawLevelField(x, y);
6722     }
6723     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6724 	     game.magnify_time_left == 0)
6725     {
6726       Feld[x][y] = EL_EMC_FAKE_GRASS;
6727       TEST_DrawLevelField(x, y);
6728     }
6729     else if (IS_GATE_GRAY(element) &&
6730 	     game.magnify_time_left > 0)
6731     {
6732       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6733 		    element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6734 		    IS_EM_GATE_GRAY(element) ?
6735 		    element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6736 		    IS_EMC_GATE_GRAY(element) ?
6737 		    element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6738 		    IS_DC_GATE_GRAY(element) ?
6739 		    EL_DC_GATE_WHITE_GRAY_ACTIVE :
6740 		    element);
6741       TEST_DrawLevelField(x, y);
6742     }
6743     else if (IS_GATE_GRAY_ACTIVE(element) &&
6744 	     game.magnify_time_left == 0)
6745     {
6746       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6747 		    element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6748 		    IS_EM_GATE_GRAY_ACTIVE(element) ?
6749 		    element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6750 		    IS_EMC_GATE_GRAY_ACTIVE(element) ?
6751 		    element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6752 		    IS_DC_GATE_GRAY_ACTIVE(element) ?
6753 		    EL_DC_GATE_WHITE_GRAY :
6754 		    element);
6755       TEST_DrawLevelField(x, y);
6756     }
6757   }
6758 }
6759 
ToggleLightSwitch(int x,int y)6760 static void ToggleLightSwitch(int x, int y)
6761 {
6762   int element = Feld[x][y];
6763 
6764   game.light_time_left =
6765     (element == EL_LIGHT_SWITCH ?
6766      level.time_light * FRAMES_PER_SECOND : 0);
6767 
6768   RedrawAllLightSwitchesAndInvisibleElements();
6769 }
6770 
ActivateTimegateSwitch(int x,int y)6771 static void ActivateTimegateSwitch(int x, int y)
6772 {
6773   int xx, yy;
6774 
6775   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6776 
6777   SCAN_PLAYFIELD(xx, yy)
6778   {
6779     int element = Feld[xx][yy];
6780 
6781     if (element == EL_TIMEGATE_CLOSED ||
6782 	element == EL_TIMEGATE_CLOSING)
6783     {
6784       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6785       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6786     }
6787 
6788     /*
6789     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6790     {
6791       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6792       TEST_DrawLevelField(xx, yy);
6793     }
6794     */
6795 
6796   }
6797 
6798 #if 1
6799   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6800 		EL_DC_TIMEGATE_SWITCH_ACTIVE);
6801 #else
6802   Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6803 #endif
6804 }
6805 
Impact(int x,int y)6806 void Impact(int x, int y)
6807 {
6808   boolean last_line = (y == lev_fieldy - 1);
6809   boolean object_hit = FALSE;
6810   boolean impact = (last_line || object_hit);
6811   int element = Feld[x][y];
6812   int smashed = EL_STEELWALL;
6813 
6814   if (!last_line)	/* check if element below was hit */
6815   {
6816     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6817       return;
6818 
6819     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6820 					 MovDir[x][y + 1] != MV_DOWN ||
6821 					 MovPos[x][y + 1] <= TILEY / 2));
6822 
6823     /* do not smash moving elements that left the smashed field in time */
6824     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6825 	ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6826       object_hit = FALSE;
6827 
6828 #if USE_QUICKSAND_IMPACT_BUGFIX
6829     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6830     {
6831       RemoveMovingField(x, y + 1);
6832       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6833       Feld[x][y + 2] = EL_ROCK;
6834       TEST_DrawLevelField(x, y + 2);
6835 
6836       object_hit = TRUE;
6837     }
6838 
6839     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6840     {
6841       RemoveMovingField(x, y + 1);
6842       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6843       Feld[x][y + 2] = EL_ROCK;
6844       TEST_DrawLevelField(x, y + 2);
6845 
6846       object_hit = TRUE;
6847     }
6848 #endif
6849 
6850     if (object_hit)
6851       smashed = MovingOrBlocked2Element(x, y + 1);
6852 
6853     impact = (last_line || object_hit);
6854   }
6855 
6856   if (!last_line && smashed == EL_ACID)	/* element falls into acid */
6857   {
6858     SplashAcid(x, y + 1);
6859     return;
6860   }
6861 
6862   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6863   /* only reset graphic animation if graphic really changes after impact */
6864   if (impact &&
6865       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6866   {
6867     ResetGfxAnimation(x, y);
6868     TEST_DrawLevelField(x, y);
6869   }
6870 
6871   if (impact && CAN_EXPLODE_IMPACT(element))
6872   {
6873     Bang(x, y);
6874     return;
6875   }
6876   else if (impact && element == EL_PEARL &&
6877 	   smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6878   {
6879     ResetGfxAnimation(x, y);
6880 
6881     Feld[x][y] = EL_PEARL_BREAKING;
6882     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6883     return;
6884   }
6885   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6886   {
6887     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6888 
6889     return;
6890   }
6891 
6892   if (impact && element == EL_AMOEBA_DROP)
6893   {
6894     if (object_hit && IS_PLAYER(x, y + 1))
6895       KillPlayerUnlessEnemyProtected(x, y + 1);
6896     else if (object_hit && smashed == EL_PENGUIN)
6897       Bang(x, y + 1);
6898     else
6899     {
6900       Feld[x][y] = EL_AMOEBA_GROWING;
6901       Store[x][y] = EL_AMOEBA_WET;
6902 
6903       ResetRandomAnimationValue(x, y);
6904     }
6905     return;
6906   }
6907 
6908   if (object_hit)		/* check which object was hit */
6909   {
6910     if ((CAN_PASS_MAGIC_WALL(element) &&
6911 	 (smashed == EL_MAGIC_WALL ||
6912 	  smashed == EL_BD_MAGIC_WALL)) ||
6913 	(CAN_PASS_DC_MAGIC_WALL(element) &&
6914 	 smashed == EL_DC_MAGIC_WALL))
6915     {
6916       int xx, yy;
6917       int activated_magic_wall =
6918 	(smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6919 	 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6920 	 EL_DC_MAGIC_WALL_ACTIVE);
6921 
6922       /* activate magic wall / mill */
6923       SCAN_PLAYFIELD(xx, yy)
6924       {
6925 	if (Feld[xx][yy] == smashed)
6926 	  Feld[xx][yy] = activated_magic_wall;
6927       }
6928 
6929       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6930       game.magic_wall_active = TRUE;
6931 
6932       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6933 			    SND_MAGIC_WALL_ACTIVATING :
6934 			    smashed == EL_BD_MAGIC_WALL ?
6935 			    SND_BD_MAGIC_WALL_ACTIVATING :
6936 			    SND_DC_MAGIC_WALL_ACTIVATING));
6937     }
6938 
6939     if (IS_PLAYER(x, y + 1))
6940     {
6941       if (CAN_SMASH_PLAYER(element))
6942       {
6943 	KillPlayerUnlessEnemyProtected(x, y + 1);
6944 	return;
6945       }
6946     }
6947     else if (smashed == EL_PENGUIN)
6948     {
6949       if (CAN_SMASH_PLAYER(element))
6950       {
6951 	Bang(x, y + 1);
6952 	return;
6953       }
6954     }
6955     else if (element == EL_BD_DIAMOND)
6956     {
6957       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6958       {
6959 	Bang(x, y + 1);
6960 	return;
6961       }
6962     }
6963     else if (((element == EL_SP_INFOTRON ||
6964 	       element == EL_SP_ZONK) &&
6965 	      (smashed == EL_SP_SNIKSNAK ||
6966 	       smashed == EL_SP_ELECTRON ||
6967 	       smashed == EL_SP_DISK_ORANGE)) ||
6968 	     (element == EL_SP_INFOTRON &&
6969 	      smashed == EL_SP_DISK_YELLOW))
6970     {
6971       Bang(x, y + 1);
6972       return;
6973     }
6974     else if (CAN_SMASH_EVERYTHING(element))
6975     {
6976       if (IS_CLASSIC_ENEMY(smashed) ||
6977 	  CAN_EXPLODE_SMASHED(smashed))
6978       {
6979 	Bang(x, y + 1);
6980 	return;
6981       }
6982       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6983       {
6984 	if (smashed == EL_LAMP ||
6985 	    smashed == EL_LAMP_ACTIVE)
6986 	{
6987 	  Bang(x, y + 1);
6988 	  return;
6989 	}
6990 	else if (smashed == EL_NUT)
6991 	{
6992 	  Feld[x][y + 1] = EL_NUT_BREAKING;
6993 	  PlayLevelSound(x, y, SND_NUT_BREAKING);
6994 	  RaiseScoreElement(EL_NUT);
6995 	  return;
6996 	}
6997 	else if (smashed == EL_PEARL)
6998 	{
6999 	  ResetGfxAnimation(x, y);
7000 
7001 	  Feld[x][y + 1] = EL_PEARL_BREAKING;
7002 	  PlayLevelSound(x, y, SND_PEARL_BREAKING);
7003 	  return;
7004 	}
7005 	else if (smashed == EL_DIAMOND)
7006 	{
7007 	  Feld[x][y + 1] = EL_DIAMOND_BREAKING;
7008 	  PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7009 	  return;
7010 	}
7011 	else if (IS_BELT_SWITCH(smashed))
7012 	{
7013 	  ToggleBeltSwitch(x, y + 1);
7014 	}
7015 	else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7016 		 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7017 		 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7018 		 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7019 	{
7020 	  ToggleSwitchgateSwitch(x, y + 1);
7021 	}
7022 	else if (smashed == EL_LIGHT_SWITCH ||
7023 		 smashed == EL_LIGHT_SWITCH_ACTIVE)
7024 	{
7025 	  ToggleLightSwitch(x, y + 1);
7026 	}
7027 	else
7028 	{
7029 #if 0
7030 	  TestIfElementSmashesCustomElement(x, y, MV_DOWN);
7031 #endif
7032 
7033 	  CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7034 
7035 	  CheckElementChangeBySide(x, y + 1, smashed, element,
7036 				   CE_SWITCHED, CH_SIDE_TOP);
7037 	  CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7038 					    CH_SIDE_TOP);
7039 	}
7040       }
7041       else
7042       {
7043 	CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7044       }
7045     }
7046   }
7047 
7048   /* play sound of magic wall / mill */
7049   if (!last_line &&
7050       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7051        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7052        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7053   {
7054     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7055       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7056     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7057       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7058     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7059       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7060 
7061     return;
7062   }
7063 
7064   /* play sound of object that hits the ground */
7065   if (last_line || object_hit)
7066     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7067 }
7068 
TurnRoundExt(int x,int y)7069 inline static void TurnRoundExt(int x, int y)
7070 {
7071   static struct
7072   {
7073     int dx, dy;
7074   } move_xy[] =
7075   {
7076     {  0,  0 },
7077     { -1,  0 },
7078     { +1,  0 },
7079     {  0,  0 },
7080     {  0, -1 },
7081     {  0,  0 }, { 0, 0 }, { 0, 0 },
7082     {  0, +1 }
7083   };
7084   static struct
7085   {
7086     int left, right, back;
7087   } turn[] =
7088   {
7089     { 0,	0,		0	 },
7090     { MV_DOWN,	MV_UP,		MV_RIGHT },
7091     { MV_UP,	MV_DOWN,	MV_LEFT	 },
7092     { 0,	0,		0	 },
7093     { MV_LEFT,	MV_RIGHT,	MV_DOWN	 },
7094     { 0,	0,		0	 },
7095     { 0,	0,		0	 },
7096     { 0,	0,		0	 },
7097     { MV_RIGHT,	MV_LEFT,	MV_UP	 }
7098   };
7099 
7100   int element = Feld[x][y];
7101   int move_pattern = element_info[element].move_pattern;
7102 
7103   int old_move_dir = MovDir[x][y];
7104   int left_dir  = turn[old_move_dir].left;
7105   int right_dir = turn[old_move_dir].right;
7106   int back_dir  = turn[old_move_dir].back;
7107 
7108   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7109   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7110   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7111   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7112 
7113   int left_x  = x + left_dx,  left_y  = y + left_dy;
7114   int right_x = x + right_dx, right_y = y + right_dy;
7115   int move_x  = x + move_dx,  move_y  = y + move_dy;
7116 
7117   int xx, yy;
7118 
7119   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7120   {
7121     TestIfBadThingTouchesOtherBadThing(x, y);
7122 
7123     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7124       MovDir[x][y] = right_dir;
7125     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7126       MovDir[x][y] = left_dir;
7127 
7128     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7129       MovDelay[x][y] = 9;
7130     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
7131       MovDelay[x][y] = 1;
7132   }
7133   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7134   {
7135     TestIfBadThingTouchesOtherBadThing(x, y);
7136 
7137     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7138       MovDir[x][y] = left_dir;
7139     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7140       MovDir[x][y] = right_dir;
7141 
7142     if (element == EL_SPACESHIP	&& MovDir[x][y] != old_move_dir)
7143       MovDelay[x][y] = 9;
7144     else if (element == EL_BD_FIREFLY)	    /* && MovDir[x][y] == right_dir) */
7145       MovDelay[x][y] = 1;
7146   }
7147   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7148   {
7149     TestIfBadThingTouchesOtherBadThing(x, y);
7150 
7151     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7152       MovDir[x][y] = left_dir;
7153     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7154       MovDir[x][y] = right_dir;
7155 
7156     if (MovDir[x][y] != old_move_dir)
7157       MovDelay[x][y] = 9;
7158   }
7159   else if (element == EL_YAMYAM)
7160   {
7161     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7162     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7163 
7164     if (can_turn_left && can_turn_right)
7165       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7166     else if (can_turn_left)
7167       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7168     else if (can_turn_right)
7169       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7170     else
7171       MovDir[x][y] = back_dir;
7172 
7173     MovDelay[x][y] = 16 + 16 * RND(3);
7174   }
7175   else if (element == EL_DARK_YAMYAM)
7176   {
7177     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7178 							 left_x, left_y);
7179     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7180 							 right_x, right_y);
7181 
7182     if (can_turn_left && can_turn_right)
7183       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7184     else if (can_turn_left)
7185       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7186     else if (can_turn_right)
7187       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7188     else
7189       MovDir[x][y] = back_dir;
7190 
7191     MovDelay[x][y] = 16 + 16 * RND(3);
7192   }
7193   else if (element == EL_PACMAN)
7194   {
7195     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7196     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7197 
7198     if (can_turn_left && can_turn_right)
7199       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7200     else if (can_turn_left)
7201       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7202     else if (can_turn_right)
7203       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7204     else
7205       MovDir[x][y] = back_dir;
7206 
7207     MovDelay[x][y] = 6 + RND(40);
7208   }
7209   else if (element == EL_PIG)
7210   {
7211     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7212     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7213     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7214     boolean should_turn_left, should_turn_right, should_move_on;
7215     int rnd_value = 24;
7216     int rnd = RND(rnd_value);
7217 
7218     should_turn_left = (can_turn_left &&
7219 			(!can_move_on ||
7220 			 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7221 						   y + back_dy + left_dy)));
7222     should_turn_right = (can_turn_right &&
7223 			 (!can_move_on ||
7224 			  IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7225 						    y + back_dy + right_dy)));
7226     should_move_on = (can_move_on &&
7227 		      (!can_turn_left ||
7228 		       !can_turn_right ||
7229 		       IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7230 						 y + move_dy + left_dy) ||
7231 		       IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7232 						 y + move_dy + right_dy)));
7233 
7234     if (should_turn_left || should_turn_right || should_move_on)
7235     {
7236       if (should_turn_left && should_turn_right && should_move_on)
7237 	MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7238 			rnd < 2 * rnd_value / 3 ? right_dir :
7239 			old_move_dir);
7240       else if (should_turn_left && should_turn_right)
7241 	MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7242       else if (should_turn_left && should_move_on)
7243 	MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7244       else if (should_turn_right && should_move_on)
7245 	MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7246       else if (should_turn_left)
7247 	MovDir[x][y] = left_dir;
7248       else if (should_turn_right)
7249 	MovDir[x][y] = right_dir;
7250       else if (should_move_on)
7251 	MovDir[x][y] = old_move_dir;
7252     }
7253     else if (can_move_on && rnd > rnd_value / 8)
7254       MovDir[x][y] = old_move_dir;
7255     else if (can_turn_left && can_turn_right)
7256       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7257     else if (can_turn_left && rnd > rnd_value / 8)
7258       MovDir[x][y] = left_dir;
7259     else if (can_turn_right && rnd > rnd_value/8)
7260       MovDir[x][y] = right_dir;
7261     else
7262       MovDir[x][y] = back_dir;
7263 
7264     xx = x + move_xy[MovDir[x][y]].dx;
7265     yy = y + move_xy[MovDir[x][y]].dy;
7266 
7267     if (!IN_LEV_FIELD(xx, yy) ||
7268         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7269       MovDir[x][y] = old_move_dir;
7270 
7271     MovDelay[x][y] = 0;
7272   }
7273   else if (element == EL_DRAGON)
7274   {
7275     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7276     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7277     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7278     int rnd_value = 24;
7279     int rnd = RND(rnd_value);
7280 
7281     if (can_move_on && rnd > rnd_value / 8)
7282       MovDir[x][y] = old_move_dir;
7283     else if (can_turn_left && can_turn_right)
7284       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7285     else if (can_turn_left && rnd > rnd_value / 8)
7286       MovDir[x][y] = left_dir;
7287     else if (can_turn_right && rnd > rnd_value / 8)
7288       MovDir[x][y] = right_dir;
7289     else
7290       MovDir[x][y] = back_dir;
7291 
7292     xx = x + move_xy[MovDir[x][y]].dx;
7293     yy = y + move_xy[MovDir[x][y]].dy;
7294 
7295     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7296       MovDir[x][y] = old_move_dir;
7297 
7298     MovDelay[x][y] = 0;
7299   }
7300   else if (element == EL_MOLE)
7301   {
7302     boolean can_move_on =
7303       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7304 			    IS_AMOEBOID(Feld[move_x][move_y]) ||
7305 			    Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7306     if (!can_move_on)
7307     {
7308       boolean can_turn_left =
7309 	(MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7310 			      IS_AMOEBOID(Feld[left_x][left_y])));
7311 
7312       boolean can_turn_right =
7313 	(MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7314 			      IS_AMOEBOID(Feld[right_x][right_y])));
7315 
7316       if (can_turn_left && can_turn_right)
7317 	MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7318       else if (can_turn_left)
7319 	MovDir[x][y] = left_dir;
7320       else
7321 	MovDir[x][y] = right_dir;
7322     }
7323 
7324     if (MovDir[x][y] != old_move_dir)
7325       MovDelay[x][y] = 9;
7326   }
7327   else if (element == EL_BALLOON)
7328   {
7329     MovDir[x][y] = game.wind_direction;
7330     MovDelay[x][y] = 0;
7331   }
7332   else if (element == EL_SPRING)
7333   {
7334 #if USE_NEW_SPRING_BUMPER
7335     if (MovDir[x][y] & MV_HORIZONTAL)
7336     {
7337       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7338 	  !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7339       {
7340 	Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7341 	ResetGfxAnimation(move_x, move_y);
7342 	TEST_DrawLevelField(move_x, move_y);
7343 
7344 	MovDir[x][y] = back_dir;
7345       }
7346       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7347 	       SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7348 	MovDir[x][y] = MV_NONE;
7349     }
7350 #else
7351     if (MovDir[x][y] & MV_HORIZONTAL &&
7352 	(!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7353 	 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7354       MovDir[x][y] = MV_NONE;
7355 #endif
7356 
7357     MovDelay[x][y] = 0;
7358   }
7359   else if (element == EL_ROBOT ||
7360 	   element == EL_SATELLITE ||
7361 	   element == EL_PENGUIN ||
7362 	   element == EL_EMC_ANDROID)
7363   {
7364     int attr_x = -1, attr_y = -1;
7365 
7366     if (AllPlayersGone)
7367     {
7368       attr_x = ExitX;
7369       attr_y = ExitY;
7370     }
7371     else
7372     {
7373       int i;
7374 
7375       for (i = 0; i < MAX_PLAYERS; i++)
7376       {
7377 	struct PlayerInfo *player = &stored_player[i];
7378 	int jx = player->jx, jy = player->jy;
7379 
7380 	if (!player->active)
7381 	  continue;
7382 
7383 	if (attr_x == -1 ||
7384 	    ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7385 	{
7386 	  attr_x = jx;
7387 	  attr_y = jy;
7388 	}
7389       }
7390     }
7391 
7392     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7393 	(Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7394 	 game.engine_version < VERSION_IDENT(3,1,0,0)))
7395     {
7396       attr_x = ZX;
7397       attr_y = ZY;
7398     }
7399 
7400     if (element == EL_PENGUIN)
7401     {
7402       int i;
7403       static int xy[4][2] =
7404       {
7405 	{ 0, -1 },
7406 	{ -1, 0 },
7407 	{ +1, 0 },
7408 	{ 0, +1 }
7409       };
7410 
7411       for (i = 0; i < NUM_DIRECTIONS; i++)
7412       {
7413     	int ex = x + xy[i][0];
7414     	int ey = y + xy[i][1];
7415 
7416     	if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7417 				     Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7418 				     Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7419 				     Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7420 	{
7421 	  attr_x = ex;
7422  	  attr_y = ey;
7423 	  break;
7424 	}
7425       }
7426     }
7427 
7428     MovDir[x][y] = MV_NONE;
7429     if (attr_x < x)
7430       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7431     else if (attr_x > x)
7432       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7433     if (attr_y < y)
7434       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7435     else if (attr_y > y)
7436       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7437 
7438     if (element == EL_ROBOT)
7439     {
7440       int newx, newy;
7441 
7442       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7443 	MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7444       Moving2Blocked(x, y, &newx, &newy);
7445 
7446       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7447 	MovDelay[x][y] = 8 + 8 * !RND(3);
7448       else
7449 	MovDelay[x][y] = 16;
7450     }
7451     else if (element == EL_PENGUIN)
7452     {
7453       int newx, newy;
7454 
7455       MovDelay[x][y] = 1;
7456 
7457       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7458       {
7459 	boolean first_horiz = RND(2);
7460 	int new_move_dir = MovDir[x][y];
7461 
7462 	MovDir[x][y] =
7463 	  new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7464 	Moving2Blocked(x, y, &newx, &newy);
7465 
7466 	if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7467 	  return;
7468 
7469 	MovDir[x][y] =
7470 	  new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7471 	Moving2Blocked(x, y, &newx, &newy);
7472 
7473 	if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7474 	  return;
7475 
7476 	MovDir[x][y] = old_move_dir;
7477 	return;
7478       }
7479     }
7480     else if (element == EL_SATELLITE)
7481     {
7482       int newx, newy;
7483 
7484       MovDelay[x][y] = 1;
7485 
7486       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7487       {
7488 	boolean first_horiz = RND(2);
7489 	int new_move_dir = MovDir[x][y];
7490 
7491 	MovDir[x][y] =
7492 	  new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7493 	Moving2Blocked(x, y, &newx, &newy);
7494 
7495 	if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7496 	  return;
7497 
7498 	MovDir[x][y] =
7499 	  new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7500 	Moving2Blocked(x, y, &newx, &newy);
7501 
7502 	if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7503 	  return;
7504 
7505 	MovDir[x][y] = old_move_dir;
7506 	return;
7507       }
7508     }
7509     else if (element == EL_EMC_ANDROID)
7510     {
7511       static int check_pos[16] =
7512       {
7513 	-1,		/*  0 => (invalid)          */
7514 	7,		/*  1 => MV_LEFT            */
7515 	3,		/*  2 => MV_RIGHT           */
7516 	-1,		/*  3 => (invalid)          */
7517 	1,		/*  4 =>            MV_UP   */
7518 	0,		/*  5 => MV_LEFT  | MV_UP   */
7519 	2,		/*  6 => MV_RIGHT | MV_UP   */
7520 	-1,		/*  7 => (invalid)          */
7521 	5,		/*  8 =>            MV_DOWN */
7522 	6,		/*  9 => MV_LEFT  | MV_DOWN */
7523 	4,		/* 10 => MV_RIGHT | MV_DOWN */
7524 	-1,		/* 11 => (invalid)          */
7525 	-1,		/* 12 => (invalid)          */
7526 	-1,		/* 13 => (invalid)          */
7527 	-1,		/* 14 => (invalid)          */
7528 	-1,		/* 15 => (invalid)          */
7529       };
7530       static struct
7531       {
7532 	int dx, dy;
7533 	int dir;
7534       } check_xy[8] =
7535       {
7536         { -1, -1,	MV_LEFT  | MV_UP   },
7537        	{  0, -1,	           MV_UP   },
7538 	{ +1, -1,	MV_RIGHT | MV_UP   },
7539 	{ +1,  0,	MV_RIGHT           },
7540 	{ +1, +1,	MV_RIGHT | MV_DOWN },
7541 	{  0, +1,	           MV_DOWN },
7542 	{ -1, +1,	MV_LEFT  | MV_DOWN },
7543 	{ -1,  0,	MV_LEFT            },
7544       };
7545       int start_pos, check_order;
7546       boolean can_clone = FALSE;
7547       int i;
7548 
7549       /* check if there is any free field around current position */
7550       for (i = 0; i < 8; i++)
7551       {
7552 	int newx = x + check_xy[i].dx;
7553 	int newy = y + check_xy[i].dy;
7554 
7555 	if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7556 	{
7557 	  can_clone = TRUE;
7558 
7559 	  break;
7560 	}
7561       }
7562 
7563       if (can_clone)		/* randomly find an element to clone */
7564       {
7565 	can_clone = FALSE;
7566 
7567 	start_pos = check_pos[RND(8)];
7568 	check_order = (RND(2) ? -1 : +1);
7569 
7570 	for (i = 0; i < 8; i++)
7571 	{
7572 	  int pos_raw = start_pos + i * check_order;
7573 	  int pos = (pos_raw + 8) % 8;
7574 	  int newx = x + check_xy[pos].dx;
7575 	  int newy = y + check_xy[pos].dy;
7576 
7577 	  if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7578 	  {
7579 	    element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7580 	    element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7581 
7582 	    Store[x][y] = Feld[newx][newy];
7583 
7584 	    can_clone = TRUE;
7585 
7586 	    break;
7587 	  }
7588 	}
7589       }
7590 
7591       if (can_clone)		/* randomly find a direction to move */
7592       {
7593 	can_clone = FALSE;
7594 
7595 	start_pos = check_pos[RND(8)];
7596 	check_order = (RND(2) ? -1 : +1);
7597 
7598 	for (i = 0; i < 8; i++)
7599 	{
7600 	  int pos_raw = start_pos + i * check_order;
7601 	  int pos = (pos_raw + 8) % 8;
7602 	  int newx = x + check_xy[pos].dx;
7603 	  int newy = y + check_xy[pos].dy;
7604 	  int new_move_dir = check_xy[pos].dir;
7605 
7606 	  if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7607 	  {
7608 	    MovDir[x][y] = new_move_dir;
7609 	    MovDelay[x][y] = level.android_clone_time * 8 + 1;
7610 
7611 	    can_clone = TRUE;
7612 
7613 	    break;
7614 	  }
7615 	}
7616       }
7617 
7618       if (can_clone)		/* cloning and moving successful */
7619 	return;
7620 
7621       /* cannot clone -- try to move towards player */
7622 
7623       start_pos = check_pos[MovDir[x][y] & 0x0f];
7624       check_order = (RND(2) ? -1 : +1);
7625 
7626       for (i = 0; i < 3; i++)
7627       {
7628 	/* first check start_pos, then previous/next or (next/previous) pos */
7629 	int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7630 	int pos = (pos_raw + 8) % 8;
7631 	int newx = x + check_xy[pos].dx;
7632 	int newy = y + check_xy[pos].dy;
7633 	int new_move_dir = check_xy[pos].dir;
7634 
7635 	if (IS_PLAYER(newx, newy))
7636 	  break;
7637 
7638 	if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7639 	{
7640 	  MovDir[x][y] = new_move_dir;
7641 	  MovDelay[x][y] = level.android_move_time * 8 + 1;
7642 
7643 	  break;
7644 	}
7645       }
7646     }
7647   }
7648   else if (move_pattern == MV_TURNING_LEFT ||
7649 	   move_pattern == MV_TURNING_RIGHT ||
7650 	   move_pattern == MV_TURNING_LEFT_RIGHT ||
7651 	   move_pattern == MV_TURNING_RIGHT_LEFT ||
7652 	   move_pattern == MV_TURNING_RANDOM ||
7653 	   move_pattern == MV_ALL_DIRECTIONS)
7654   {
7655     boolean can_turn_left =
7656       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7657     boolean can_turn_right =
7658       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7659 
7660     if (element_info[element].move_stepsize == 0)	/* "not moving" */
7661       return;
7662 
7663     if (move_pattern == MV_TURNING_LEFT)
7664       MovDir[x][y] = left_dir;
7665     else if (move_pattern == MV_TURNING_RIGHT)
7666       MovDir[x][y] = right_dir;
7667     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7668       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7669     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7670       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7671     else if (move_pattern == MV_TURNING_RANDOM)
7672       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7673 		      can_turn_right && !can_turn_left ? right_dir :
7674 		      RND(2) ? left_dir : right_dir);
7675     else if (can_turn_left && can_turn_right)
7676       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7677     else if (can_turn_left)
7678       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7679     else if (can_turn_right)
7680       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7681     else
7682       MovDir[x][y] = back_dir;
7683 
7684     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7685   }
7686   else if (move_pattern == MV_HORIZONTAL ||
7687 	   move_pattern == MV_VERTICAL)
7688   {
7689     if (move_pattern & old_move_dir)
7690       MovDir[x][y] = back_dir;
7691     else if (move_pattern == MV_HORIZONTAL)
7692       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7693     else if (move_pattern == MV_VERTICAL)
7694       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7695 
7696     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7697   }
7698   else if (move_pattern & MV_ANY_DIRECTION)
7699   {
7700     MovDir[x][y] = move_pattern;
7701     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7702   }
7703   else if (move_pattern & MV_WIND_DIRECTION)
7704   {
7705     MovDir[x][y] = game.wind_direction;
7706     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7707   }
7708   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7709   {
7710     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7711       MovDir[x][y] = left_dir;
7712     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7713       MovDir[x][y] = right_dir;
7714 
7715     if (MovDir[x][y] != old_move_dir)
7716       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7717   }
7718   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7719   {
7720     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7721       MovDir[x][y] = right_dir;
7722     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7723       MovDir[x][y] = left_dir;
7724 
7725     if (MovDir[x][y] != old_move_dir)
7726       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7727   }
7728   else if (move_pattern == MV_TOWARDS_PLAYER ||
7729 	   move_pattern == MV_AWAY_FROM_PLAYER)
7730   {
7731     int attr_x = -1, attr_y = -1;
7732     int newx, newy;
7733     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7734 
7735     if (AllPlayersGone)
7736     {
7737       attr_x = ExitX;
7738       attr_y = ExitY;
7739     }
7740     else
7741     {
7742       int i;
7743 
7744       for (i = 0; i < MAX_PLAYERS; i++)
7745       {
7746 	struct PlayerInfo *player = &stored_player[i];
7747 	int jx = player->jx, jy = player->jy;
7748 
7749 	if (!player->active)
7750 	  continue;
7751 
7752 	if (attr_x == -1 ||
7753 	    ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7754 	{
7755 	  attr_x = jx;
7756 	  attr_y = jy;
7757 	}
7758       }
7759     }
7760 
7761     MovDir[x][y] = MV_NONE;
7762     if (attr_x < x)
7763       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7764     else if (attr_x > x)
7765       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7766     if (attr_y < y)
7767       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7768     else if (attr_y > y)
7769       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7770 
7771     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7772 
7773     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7774     {
7775       boolean first_horiz = RND(2);
7776       int new_move_dir = MovDir[x][y];
7777 
7778       if (element_info[element].move_stepsize == 0)	/* "not moving" */
7779       {
7780 	first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7781 	MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7782 
7783 	return;
7784       }
7785 
7786       MovDir[x][y] =
7787 	new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7788       Moving2Blocked(x, y, &newx, &newy);
7789 
7790       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7791 	return;
7792 
7793       MovDir[x][y] =
7794 	new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7795       Moving2Blocked(x, y, &newx, &newy);
7796 
7797       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7798 	return;
7799 
7800       MovDir[x][y] = old_move_dir;
7801     }
7802   }
7803   else if (move_pattern == MV_WHEN_PUSHED ||
7804 	   move_pattern == MV_WHEN_DROPPED)
7805   {
7806     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7807       MovDir[x][y] = MV_NONE;
7808 
7809     MovDelay[x][y] = 0;
7810   }
7811   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7812   {
7813     static int test_xy[7][2] =
7814     {
7815       { 0, -1 },
7816       { -1, 0 },
7817       { +1, 0 },
7818       { 0, +1 },
7819       { 0, -1 },
7820       { -1, 0 },
7821       { +1, 0 },
7822     };
7823     static int test_dir[7] =
7824     {
7825       MV_UP,
7826       MV_LEFT,
7827       MV_RIGHT,
7828       MV_DOWN,
7829       MV_UP,
7830       MV_LEFT,
7831       MV_RIGHT,
7832     };
7833     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7834     int move_preference = -1000000;	/* start with very low preference */
7835     int new_move_dir = MV_NONE;
7836     int start_test = RND(4);
7837     int i;
7838 
7839     for (i = 0; i < NUM_DIRECTIONS; i++)
7840     {
7841       int move_dir = test_dir[start_test + i];
7842       int move_dir_preference;
7843 
7844       xx = x + test_xy[start_test + i][0];
7845       yy = y + test_xy[start_test + i][1];
7846 
7847       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7848 	  (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7849       {
7850 	new_move_dir = move_dir;
7851 
7852 	break;
7853       }
7854 
7855       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7856 	continue;
7857 
7858       move_dir_preference = -1 * RunnerVisit[xx][yy];
7859       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7860 	move_dir_preference = PlayerVisit[xx][yy];
7861 
7862       if (move_dir_preference > move_preference)
7863       {
7864 	/* prefer field that has not been visited for the longest time */
7865 	move_preference = move_dir_preference;
7866 	new_move_dir = move_dir;
7867       }
7868       else if (move_dir_preference == move_preference &&
7869 	       move_dir == old_move_dir)
7870       {
7871 	/* prefer last direction when all directions are preferred equally */
7872 	move_preference = move_dir_preference;
7873 	new_move_dir = move_dir;
7874       }
7875     }
7876 
7877     MovDir[x][y] = new_move_dir;
7878     if (old_move_dir != new_move_dir)
7879       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7880   }
7881 }
7882 
TurnRound(int x,int y)7883 static void TurnRound(int x, int y)
7884 {
7885   int direction = MovDir[x][y];
7886 
7887   TurnRoundExt(x, y);
7888 
7889   GfxDir[x][y] = MovDir[x][y];
7890 
7891   if (direction != MovDir[x][y])
7892     GfxFrame[x][y] = 0;
7893 
7894   if (MovDelay[x][y])
7895     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7896 
7897   ResetGfxFrame(x, y, FALSE);
7898 }
7899 
JustBeingPushed(int x,int y)7900 static boolean JustBeingPushed(int x, int y)
7901 {
7902   int i;
7903 
7904   for (i = 0; i < MAX_PLAYERS; i++)
7905   {
7906     struct PlayerInfo *player = &stored_player[i];
7907 
7908     if (player->active && player->is_pushing && player->MovPos)
7909     {
7910       int next_jx = player->jx + (player->jx - player->last_jx);
7911       int next_jy = player->jy + (player->jy - player->last_jy);
7912 
7913       if (x == next_jx && y == next_jy)
7914 	return TRUE;
7915     }
7916   }
7917 
7918   return FALSE;
7919 }
7920 
StartMoving(int x,int y)7921 void StartMoving(int x, int y)
7922 {
7923   boolean started_moving = FALSE;	/* some elements can fall _and_ move */
7924   int element = Feld[x][y];
7925 
7926   if (Stop[x][y])
7927     return;
7928 
7929   if (MovDelay[x][y] == 0)
7930     GfxAction[x][y] = ACTION_DEFAULT;
7931 
7932   if (CAN_FALL(element) && y < lev_fieldy - 1)
7933   {
7934     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7935 	(x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7936       if (JustBeingPushed(x, y))
7937 	return;
7938 
7939     if (element == EL_QUICKSAND_FULL)
7940     {
7941       if (IS_FREE(x, y + 1))
7942       {
7943 	InitMovingField(x, y, MV_DOWN);
7944 	started_moving = TRUE;
7945 
7946 	Feld[x][y] = EL_QUICKSAND_EMPTYING;
7947 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7948 	if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7949 	  Store[x][y] = EL_ROCK;
7950 #else
7951 	Store[x][y] = EL_ROCK;
7952 #endif
7953 
7954 	PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7955       }
7956       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7957       {
7958 	if (!MovDelay[x][y])
7959 	{
7960 	  MovDelay[x][y] = TILEY + 1;
7961 
7962 	  ResetGfxAnimation(x, y);
7963 	  ResetGfxAnimation(x, y + 1);
7964 	}
7965 
7966 	if (MovDelay[x][y])
7967 	{
7968 	  DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7969 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7970 
7971 	  MovDelay[x][y]--;
7972 	  if (MovDelay[x][y])
7973 	    return;
7974 	}
7975 
7976 	Feld[x][y] = EL_QUICKSAND_EMPTY;
7977 	Feld[x][y + 1] = EL_QUICKSAND_FULL;
7978 	Store[x][y + 1] = Store[x][y];
7979 	Store[x][y] = 0;
7980 
7981 	PlayLevelSoundAction(x, y, ACTION_FILLING);
7982       }
7983       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7984       {
7985 	if (!MovDelay[x][y])
7986 	{
7987 	  MovDelay[x][y] = TILEY + 1;
7988 
7989 	  ResetGfxAnimation(x, y);
7990 	  ResetGfxAnimation(x, y + 1);
7991 	}
7992 
7993 	if (MovDelay[x][y])
7994 	{
7995 	  DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7996 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7997 
7998 	  MovDelay[x][y]--;
7999 	  if (MovDelay[x][y])
8000 	    return;
8001 	}
8002 
8003 	Feld[x][y] = EL_QUICKSAND_EMPTY;
8004 	Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8005 	Store[x][y + 1] = Store[x][y];
8006 	Store[x][y] = 0;
8007 
8008 	PlayLevelSoundAction(x, y, ACTION_FILLING);
8009       }
8010     }
8011     else if (element == EL_QUICKSAND_FAST_FULL)
8012     {
8013       if (IS_FREE(x, y + 1))
8014       {
8015 	InitMovingField(x, y, MV_DOWN);
8016 	started_moving = TRUE;
8017 
8018 	Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8019 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8020 	if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8021 	  Store[x][y] = EL_ROCK;
8022 #else
8023 	Store[x][y] = EL_ROCK;
8024 #endif
8025 
8026 	PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8027       }
8028       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8029       {
8030 	if (!MovDelay[x][y])
8031 	{
8032 	  MovDelay[x][y] = TILEY + 1;
8033 
8034 	  ResetGfxAnimation(x, y);
8035 	  ResetGfxAnimation(x, y + 1);
8036 	}
8037 
8038 	if (MovDelay[x][y])
8039 	{
8040 	  DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8041 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8042 
8043 	  MovDelay[x][y]--;
8044 	  if (MovDelay[x][y])
8045 	    return;
8046 	}
8047 
8048 	Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8049 	Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8050 	Store[x][y + 1] = Store[x][y];
8051 	Store[x][y] = 0;
8052 
8053 	PlayLevelSoundAction(x, y, ACTION_FILLING);
8054       }
8055       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8056       {
8057 	if (!MovDelay[x][y])
8058 	{
8059 	  MovDelay[x][y] = TILEY + 1;
8060 
8061 	  ResetGfxAnimation(x, y);
8062 	  ResetGfxAnimation(x, y + 1);
8063 	}
8064 
8065 	if (MovDelay[x][y])
8066 	{
8067 	  DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8068 	  DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8069 
8070 	  MovDelay[x][y]--;
8071 	  if (MovDelay[x][y])
8072 	    return;
8073 	}
8074 
8075 	Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8076 	Feld[x][y + 1] = EL_QUICKSAND_FULL;
8077 	Store[x][y + 1] = Store[x][y];
8078 	Store[x][y] = 0;
8079 
8080 	PlayLevelSoundAction(x, y, ACTION_FILLING);
8081       }
8082     }
8083     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8084 	     Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8085     {
8086       InitMovingField(x, y, MV_DOWN);
8087       started_moving = TRUE;
8088 
8089       Feld[x][y] = EL_QUICKSAND_FILLING;
8090       Store[x][y] = element;
8091 
8092       PlayLevelSoundAction(x, y, ACTION_FILLING);
8093     }
8094     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8095 	     Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8096     {
8097       InitMovingField(x, y, MV_DOWN);
8098       started_moving = TRUE;
8099 
8100       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8101       Store[x][y] = element;
8102 
8103       PlayLevelSoundAction(x, y, ACTION_FILLING);
8104     }
8105     else if (element == EL_MAGIC_WALL_FULL)
8106     {
8107       if (IS_FREE(x, y + 1))
8108       {
8109 	InitMovingField(x, y, MV_DOWN);
8110 	started_moving = TRUE;
8111 
8112 	Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8113 	Store[x][y] = EL_CHANGED(Store[x][y]);
8114       }
8115       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8116       {
8117 	if (!MovDelay[x][y])
8118 	  MovDelay[x][y] = TILEY/4 + 1;
8119 
8120 	if (MovDelay[x][y])
8121 	{
8122 	  MovDelay[x][y]--;
8123 	  if (MovDelay[x][y])
8124 	    return;
8125 	}
8126 
8127 	Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8128 	Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8129 	Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8130 	Store[x][y] = 0;
8131       }
8132     }
8133     else if (element == EL_BD_MAGIC_WALL_FULL)
8134     {
8135       if (IS_FREE(x, y + 1))
8136       {
8137 	InitMovingField(x, y, MV_DOWN);
8138 	started_moving = TRUE;
8139 
8140 	Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8141 	Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8142       }
8143       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8144       {
8145 	if (!MovDelay[x][y])
8146 	  MovDelay[x][y] = TILEY/4 + 1;
8147 
8148 	if (MovDelay[x][y])
8149 	{
8150 	  MovDelay[x][y]--;
8151 	  if (MovDelay[x][y])
8152 	    return;
8153 	}
8154 
8155 	Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8156 	Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8157 	Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8158 	Store[x][y] = 0;
8159       }
8160     }
8161     else if (element == EL_DC_MAGIC_WALL_FULL)
8162     {
8163       if (IS_FREE(x, y + 1))
8164       {
8165 	InitMovingField(x, y, MV_DOWN);
8166 	started_moving = TRUE;
8167 
8168 	Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8169 	Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8170       }
8171       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8172       {
8173 	if (!MovDelay[x][y])
8174 	  MovDelay[x][y] = TILEY/4 + 1;
8175 
8176 	if (MovDelay[x][y])
8177 	{
8178 	  MovDelay[x][y]--;
8179 	  if (MovDelay[x][y])
8180 	    return;
8181 	}
8182 
8183 	Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8184 	Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8185 	Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8186 	Store[x][y] = 0;
8187       }
8188     }
8189     else if ((CAN_PASS_MAGIC_WALL(element) &&
8190 	      (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8191 	       Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8192 	     (CAN_PASS_DC_MAGIC_WALL(element) &&
8193 	      (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8194 
8195     {
8196       InitMovingField(x, y, MV_DOWN);
8197       started_moving = TRUE;
8198 
8199       Feld[x][y] =
8200 	(Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8201 	 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8202 	 EL_DC_MAGIC_WALL_FILLING);
8203       Store[x][y] = element;
8204     }
8205     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8206     {
8207       SplashAcid(x, y + 1);
8208 
8209       InitMovingField(x, y, MV_DOWN);
8210       started_moving = TRUE;
8211 
8212       Store[x][y] = EL_ACID;
8213     }
8214     else if (
8215 #if USE_FIX_IMPACT_COLLISION
8216 	     (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8217 	      CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8218 #else
8219 	     (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8220 	      CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8221 #endif
8222 	     (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8223 	      CAN_FALL(element) && WasJustFalling[x][y] &&
8224 	      (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8225 
8226 	     (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8227 	      CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8228 	      (Feld[x][y + 1] == EL_BLOCKED)))
8229     {
8230       /* this is needed for a special case not covered by calling "Impact()"
8231 	 from "ContinueMoving()": if an element moves to a tile directly below
8232 	 another element which was just falling on that tile (which was empty
8233 	 in the previous frame), the falling element above would just stop
8234 	 instead of smashing the element below (in previous version, the above
8235 	 element was just checked for "moving" instead of "falling", resulting
8236 	 in incorrect smashes caused by horizontal movement of the above
8237 	 element; also, the case of the player being the element to smash was
8238 	 simply not covered here... :-/ ) */
8239 
8240       CheckCollision[x][y] = 0;
8241       CheckImpact[x][y] = 0;
8242 
8243       Impact(x, y);
8244     }
8245     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8246     {
8247       if (MovDir[x][y] == MV_NONE)
8248       {
8249 	InitMovingField(x, y, MV_DOWN);
8250 	started_moving = TRUE;
8251       }
8252     }
8253     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8254     {
8255       if (WasJustFalling[x][y])	/* prevent animation from being restarted */
8256 	MovDir[x][y] = MV_DOWN;
8257 
8258       InitMovingField(x, y, MV_DOWN);
8259       started_moving = TRUE;
8260     }
8261     else if (element == EL_AMOEBA_DROP)
8262     {
8263       Feld[x][y] = EL_AMOEBA_GROWING;
8264       Store[x][y] = EL_AMOEBA_WET;
8265     }
8266     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8267 	      (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8268 	     !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8269 	     element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8270     {
8271       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8272 				(IS_FREE(x - 1, y + 1) ||
8273 				 Feld[x - 1][y + 1] == EL_ACID));
8274       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8275 				(IS_FREE(x + 1, y + 1) ||
8276 				 Feld[x + 1][y + 1] == EL_ACID));
8277       boolean can_fall_any  = (can_fall_left || can_fall_right);
8278       boolean can_fall_both = (can_fall_left && can_fall_right);
8279       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8280 
8281 #if USE_NEW_ALL_SLIPPERY
8282       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8283       {
8284 	if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8285 	  can_fall_right = FALSE;
8286 	else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8287 	  can_fall_left = FALSE;
8288 	else if (slippery_type == SLIPPERY_ONLY_LEFT)
8289 	  can_fall_right = FALSE;
8290 	else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8291 	  can_fall_left = FALSE;
8292 
8293 	can_fall_any  = (can_fall_left || can_fall_right);
8294 	can_fall_both = FALSE;
8295       }
8296 #else
8297       if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8298       {
8299 	if (slippery_type == SLIPPERY_ONLY_LEFT)
8300 	  can_fall_right = FALSE;
8301 	else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8302 	  can_fall_left = FALSE;
8303 	else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8304 	  can_fall_right = FALSE;
8305 	else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8306 	  can_fall_left = FALSE;
8307 
8308 	can_fall_any  = (can_fall_left || can_fall_right);
8309 	can_fall_both = (can_fall_left && can_fall_right);
8310       }
8311 #endif
8312 
8313 #if USE_NEW_ALL_SLIPPERY
8314 #else
8315 #if USE_NEW_SP_SLIPPERY
8316       /* !!! better use the same properties as for custom elements here !!! */
8317       else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8318 	       can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8319       {
8320 	can_fall_right = FALSE;		/* slip down on left side */
8321 	can_fall_both = FALSE;
8322       }
8323 #endif
8324 #endif
8325 
8326 #if USE_NEW_ALL_SLIPPERY
8327       if (can_fall_both)
8328       {
8329 	if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8330 	  can_fall_right = FALSE;	/* slip down on left side */
8331 	else
8332 	  can_fall_left = !(can_fall_right = RND(2));
8333 
8334 	can_fall_both = FALSE;
8335       }
8336 #else
8337       if (can_fall_both)
8338       {
8339 	if (game.emulation == EMU_BOULDERDASH ||
8340 	    element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8341 	  can_fall_right = FALSE;	/* slip down on left side */
8342 	else
8343 	  can_fall_left = !(can_fall_right = RND(2));
8344 
8345 	can_fall_both = FALSE;
8346       }
8347 #endif
8348 
8349       if (can_fall_any)
8350       {
8351 	/* if not determined otherwise, prefer left side for slipping down */
8352 	InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8353 	started_moving = TRUE;
8354       }
8355     }
8356 #if 0
8357     else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8358 #else
8359     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8360 #endif
8361     {
8362       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8363       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8364       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8365       int belt_dir = game.belt_dir[belt_nr];
8366 
8367       if ((belt_dir == MV_LEFT  && left_is_free) ||
8368 	  (belt_dir == MV_RIGHT && right_is_free))
8369       {
8370 	int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8371 
8372 	InitMovingField(x, y, belt_dir);
8373 	started_moving = TRUE;
8374 
8375 	Pushed[x][y] = TRUE;
8376 	Pushed[nextx][y] = TRUE;
8377 
8378 	GfxAction[x][y] = ACTION_DEFAULT;
8379       }
8380       else
8381       {
8382 	MovDir[x][y] = 0;	/* if element was moving, stop it */
8383       }
8384     }
8385   }
8386 
8387   /* not "else if" because of elements that can fall and move (EL_SPRING) */
8388 #if 0
8389   if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8390 #else
8391   if (CAN_MOVE(element) && !started_moving)
8392 #endif
8393   {
8394     int move_pattern = element_info[element].move_pattern;
8395     int newx, newy;
8396 
8397 #if 0
8398 #if DEBUG
8399     if (MovDir[x][y] == MV_NONE)
8400     {
8401       printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8402 	     x, y, element, element_info[element].token_name);
8403       printf("StartMoving(): This should never happen!\n");
8404     }
8405 #endif
8406 #endif
8407 
8408     Moving2Blocked(x, y, &newx, &newy);
8409 
8410     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8411       return;
8412 
8413     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8414 	CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8415     {
8416       WasJustMoving[x][y] = 0;
8417       CheckCollision[x][y] = 0;
8418 
8419       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8420 
8421       if (Feld[x][y] != element)	/* element has changed */
8422 	return;
8423     }
8424 
8425     if (!MovDelay[x][y])	/* start new movement phase */
8426     {
8427       /* all objects that can change their move direction after each step
8428 	 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8429 
8430       if (element != EL_YAMYAM &&
8431 	  element != EL_DARK_YAMYAM &&
8432 	  element != EL_PACMAN &&
8433 	  !(move_pattern & MV_ANY_DIRECTION) &&
8434 	  move_pattern != MV_TURNING_LEFT &&
8435 	  move_pattern != MV_TURNING_RIGHT &&
8436 	  move_pattern != MV_TURNING_LEFT_RIGHT &&
8437 	  move_pattern != MV_TURNING_RIGHT_LEFT &&
8438 	  move_pattern != MV_TURNING_RANDOM)
8439       {
8440 	TurnRound(x, y);
8441 
8442 	if (MovDelay[x][y] && (element == EL_BUG ||
8443 			       element == EL_SPACESHIP ||
8444 			       element == EL_SP_SNIKSNAK ||
8445 			       element == EL_SP_ELECTRON ||
8446 			       element == EL_MOLE))
8447 	  TEST_DrawLevelField(x, y);
8448       }
8449     }
8450 
8451     if (MovDelay[x][y])		/* wait some time before next movement */
8452     {
8453       MovDelay[x][y]--;
8454 
8455       if (element == EL_ROBOT ||
8456 	  element == EL_YAMYAM ||
8457 	  element == EL_DARK_YAMYAM)
8458       {
8459 	DrawLevelElementAnimationIfNeeded(x, y, element);
8460 	PlayLevelSoundAction(x, y, ACTION_WAITING);
8461       }
8462       else if (element == EL_SP_ELECTRON)
8463 	DrawLevelElementAnimationIfNeeded(x, y, element);
8464       else if (element == EL_DRAGON)
8465       {
8466 	int i;
8467 	int dir = MovDir[x][y];
8468 	int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8469 	int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8470 	int graphic = (dir == MV_LEFT	? IMG_FLAMES_1_LEFT :
8471 		       dir == MV_RIGHT	? IMG_FLAMES_1_RIGHT :
8472 		       dir == MV_UP	? IMG_FLAMES_1_UP :
8473 		       dir == MV_DOWN	? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8474 	int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8475 
8476 	GfxAction[x][y] = ACTION_ATTACKING;
8477 
8478 	if (IS_PLAYER(x, y))
8479 	  DrawPlayerField(x, y);
8480 	else
8481 	  TEST_DrawLevelField(x, y);
8482 
8483 	PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8484 
8485 	for (i = 1; i <= 3; i++)
8486 	{
8487 	  int xx = x + i * dx;
8488 	  int yy = y + i * dy;
8489 	  int sx = SCREENX(xx);
8490 	  int sy = SCREENY(yy);
8491 	  int flame_graphic = graphic + (i - 1);
8492 
8493 	  if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8494 	    break;
8495 
8496 	  if (MovDelay[x][y])
8497 	  {
8498 	    int flamed = MovingOrBlocked2Element(xx, yy);
8499 
8500 	    /* !!! */
8501 #if 0
8502 	    if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8503 	      Bang(xx, yy);
8504 	    else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8505 	      RemoveMovingField(xx, yy);
8506 	    else
8507 	      RemoveField(xx, yy);
8508 #else
8509 	    if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8510 	      Bang(xx, yy);
8511 	    else
8512 	      RemoveMovingField(xx, yy);
8513 #endif
8514 
8515 	    ChangeDelay[xx][yy] = 0;
8516 
8517 	    Feld[xx][yy] = EL_FLAMES;
8518 
8519 	    if (IN_SCR_FIELD(sx, sy))
8520 	    {
8521 	      TEST_DrawLevelFieldCrumbled(xx, yy);
8522 	      DrawGraphic(sx, sy, flame_graphic, frame);
8523 	    }
8524 	  }
8525 	  else
8526 	  {
8527 	    if (Feld[xx][yy] == EL_FLAMES)
8528 	      Feld[xx][yy] = EL_EMPTY;
8529 	    TEST_DrawLevelField(xx, yy);
8530 	  }
8531 	}
8532       }
8533 
8534       if (MovDelay[x][y])	/* element still has to wait some time */
8535       {
8536 	PlayLevelSoundAction(x, y, ACTION_WAITING);
8537 
8538 	return;
8539       }
8540     }
8541 
8542     /* now make next step */
8543 
8544     Moving2Blocked(x, y, &newx, &newy);	/* get next screen position */
8545 
8546     if (DONT_COLLIDE_WITH(element) &&
8547 	IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8548 	!PLAYER_ENEMY_PROTECTED(newx, newy))
8549     {
8550       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8551 
8552       return;
8553     }
8554 
8555     else if (CAN_MOVE_INTO_ACID(element) &&
8556 	     IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8557 	     !IS_MV_DIAGONAL(MovDir[x][y]) &&
8558 	     (MovDir[x][y] == MV_DOWN ||
8559 	      game.engine_version >= VERSION_IDENT(3,1,0,0)))
8560     {
8561       SplashAcid(newx, newy);
8562       Store[x][y] = EL_ACID;
8563     }
8564     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8565     {
8566       if (Feld[newx][newy] == EL_EXIT_OPEN ||
8567 	  Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8568 	  Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8569 	  Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8570       {
8571 	RemoveField(x, y);
8572 	TEST_DrawLevelField(x, y);
8573 
8574 	PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8575 	if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8576 	  DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8577 
8578 	local_player->friends_still_needed--;
8579 	if (!local_player->friends_still_needed &&
8580 	    !local_player->GameOver && AllPlayersGone)
8581 	  PlayerWins(local_player);
8582 
8583 	return;
8584       }
8585       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8586       {
8587 	if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8588 	  TEST_DrawLevelField(newx, newy);
8589 	else
8590 	  GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8591       }
8592       else if (!IS_FREE(newx, newy))
8593       {
8594 	GfxAction[x][y] = ACTION_WAITING;
8595 
8596 	if (IS_PLAYER(x, y))
8597 	  DrawPlayerField(x, y);
8598 	else
8599 	  TEST_DrawLevelField(x, y);
8600 
8601 	return;
8602       }
8603     }
8604     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8605     {
8606       if (IS_FOOD_PIG(Feld[newx][newy]))
8607       {
8608 	if (IS_MOVING(newx, newy))
8609 	  RemoveMovingField(newx, newy);
8610 	else
8611 	{
8612 	  Feld[newx][newy] = EL_EMPTY;
8613 	  TEST_DrawLevelField(newx, newy);
8614 	}
8615 
8616 	PlayLevelSound(x, y, SND_PIG_DIGGING);
8617       }
8618       else if (!IS_FREE(newx, newy))
8619       {
8620 	if (IS_PLAYER(x, y))
8621 	  DrawPlayerField(x, y);
8622 	else
8623 	  TEST_DrawLevelField(x, y);
8624 
8625 	return;
8626       }
8627     }
8628     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8629     {
8630       if (Store[x][y] != EL_EMPTY)
8631       {
8632 	boolean can_clone = FALSE;
8633 	int xx, yy;
8634 
8635 	/* check if element to clone is still there */
8636 	for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8637 	{
8638 	  if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8639 	  {
8640 	    can_clone = TRUE;
8641 
8642 	    break;
8643 	  }
8644 	}
8645 
8646 	/* cannot clone or target field not free anymore -- do not clone */
8647 	if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8648 	  Store[x][y] = EL_EMPTY;
8649       }
8650 
8651       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8652       {
8653 	if (IS_MV_DIAGONAL(MovDir[x][y]))
8654 	{
8655 	  int diagonal_move_dir = MovDir[x][y];
8656 	  int stored = Store[x][y];
8657 	  int change_delay = 8;
8658 	  int graphic;
8659 
8660 	  /* android is moving diagonally */
8661 
8662 	  CreateField(x, y, EL_DIAGONAL_SHRINKING);
8663 
8664 	  Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8665 	  GfxElement[x][y] = EL_EMC_ANDROID;
8666 	  GfxAction[x][y] = ACTION_SHRINKING;
8667 	  GfxDir[x][y] = diagonal_move_dir;
8668 	  ChangeDelay[x][y] = change_delay;
8669 
8670 	  graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8671 				   GfxDir[x][y]);
8672 
8673 	  DrawLevelGraphicAnimation(x, y, graphic);
8674 	  PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8675 
8676 	  if (Feld[newx][newy] == EL_ACID)
8677 	  {
8678 	    SplashAcid(newx, newy);
8679 
8680 	    return;
8681 	  }
8682 
8683 	  CreateField(newx, newy, EL_DIAGONAL_GROWING);
8684 
8685 	  Store[newx][newy] = EL_EMC_ANDROID;
8686 	  GfxElement[newx][newy] = EL_EMC_ANDROID;
8687 	  GfxAction[newx][newy] = ACTION_GROWING;
8688 	  GfxDir[newx][newy] = diagonal_move_dir;
8689 	  ChangeDelay[newx][newy] = change_delay;
8690 
8691 	  graphic = el_act_dir2img(GfxElement[newx][newy],
8692 				   GfxAction[newx][newy], GfxDir[newx][newy]);
8693 
8694 	  DrawLevelGraphicAnimation(newx, newy, graphic);
8695 	  PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8696 
8697 	  return;
8698 	}
8699 	else
8700 	{
8701 	  Feld[newx][newy] = EL_EMPTY;
8702 	  TEST_DrawLevelField(newx, newy);
8703 
8704 	  PlayLevelSoundAction(x, y, ACTION_DIGGING);
8705 	}
8706       }
8707       else if (!IS_FREE(newx, newy))
8708       {
8709 #if 0
8710 	if (IS_PLAYER(x, y))
8711 	  DrawPlayerField(x, y);
8712 	else
8713 	  TEST_DrawLevelField(x, y);
8714 #endif
8715 
8716 	return;
8717       }
8718     }
8719     else if (IS_CUSTOM_ELEMENT(element) &&
8720 	     CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8721     {
8722 #if 1
8723       if (!DigFieldByCE(newx, newy, element))
8724 	return;
8725 #else
8726       int new_element = Feld[newx][newy];
8727 
8728       if (!IS_FREE(newx, newy))
8729       {
8730 	int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8731 		      IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8732 		      ACTION_BREAKING);
8733 
8734 	/* no element can dig solid indestructible elements */
8735 	if (IS_INDESTRUCTIBLE(new_element) &&
8736 	    !IS_DIGGABLE(new_element) &&
8737 	    !IS_COLLECTIBLE(new_element))
8738 	  return;
8739 
8740 	if (AmoebaNr[newx][newy] &&
8741 	    (new_element == EL_AMOEBA_FULL ||
8742 	     new_element == EL_BD_AMOEBA ||
8743 	     new_element == EL_AMOEBA_GROWING))
8744 	{
8745 	  AmoebaCnt[AmoebaNr[newx][newy]]--;
8746 	  AmoebaCnt2[AmoebaNr[newx][newy]]--;
8747 	}
8748 
8749 	if (IS_MOVING(newx, newy))
8750 	  RemoveMovingField(newx, newy);
8751 	else
8752 	{
8753 	  RemoveField(newx, newy);
8754 	  TEST_DrawLevelField(newx, newy);
8755 	}
8756 
8757 	/* if digged element was about to explode, prevent the explosion */
8758 	ExplodeField[newx][newy] = EX_TYPE_NONE;
8759 
8760 	PlayLevelSoundAction(x, y, action);
8761       }
8762 
8763       Store[newx][newy] = EL_EMPTY;
8764 
8765 #if 1
8766       /* this makes it possible to leave the removed element again */
8767       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8768 	Store[newx][newy] = new_element;
8769 #else
8770       if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8771       {
8772 	int move_leave_element = element_info[element].move_leave_element;
8773 
8774 	/* this makes it possible to leave the removed element again */
8775 	Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8776 			     new_element : move_leave_element);
8777       }
8778 #endif
8779 
8780 #endif
8781 
8782       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8783       {
8784 	RunnerVisit[x][y] = FrameCounter;
8785 	PlayerVisit[x][y] /= 8;		/* expire player visit path */
8786       }
8787     }
8788     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8789     {
8790       if (!IS_FREE(newx, newy))
8791       {
8792 	if (IS_PLAYER(x, y))
8793 	  DrawPlayerField(x, y);
8794 	else
8795 	  TEST_DrawLevelField(x, y);
8796 
8797 	return;
8798       }
8799       else
8800       {
8801 	boolean wanna_flame = !RND(10);
8802 	int dx = newx - x, dy = newy - y;
8803 	int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8804 	int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8805 	int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8806 			MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8807 	int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8808 			MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8809 
8810 	if ((wanna_flame ||
8811 	     IS_CLASSIC_ENEMY(element1) ||
8812 	     IS_CLASSIC_ENEMY(element2)) &&
8813 	    element1 != EL_DRAGON && element2 != EL_DRAGON &&
8814 	    element1 != EL_FLAMES && element2 != EL_FLAMES)
8815 	{
8816 	  ResetGfxAnimation(x, y);
8817 	  GfxAction[x][y] = ACTION_ATTACKING;
8818 
8819 	  if (IS_PLAYER(x, y))
8820 	    DrawPlayerField(x, y);
8821 	  else
8822 	    TEST_DrawLevelField(x, y);
8823 
8824 	  PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8825 
8826 	  MovDelay[x][y] = 50;
8827 
8828 	  /* !!! */
8829 #if 0
8830 	  RemoveField(newx, newy);
8831 #endif
8832 	  Feld[newx][newy] = EL_FLAMES;
8833 	  if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8834 	  {
8835 #if 0
8836 	    RemoveField(newx1, newy1);
8837 #endif
8838 	    Feld[newx1][newy1] = EL_FLAMES;
8839 	  }
8840 	  if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8841 	  {
8842 #if 0
8843 	    RemoveField(newx2, newy2);
8844 #endif
8845 	    Feld[newx2][newy2] = EL_FLAMES;
8846 	  }
8847 
8848 	  return;
8849 	}
8850       }
8851     }
8852     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8853 	     Feld[newx][newy] == EL_DIAMOND)
8854     {
8855       if (IS_MOVING(newx, newy))
8856 	RemoveMovingField(newx, newy);
8857       else
8858       {
8859 	Feld[newx][newy] = EL_EMPTY;
8860 	TEST_DrawLevelField(newx, newy);
8861       }
8862 
8863       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8864     }
8865     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8866 	     IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8867     {
8868       if (AmoebaNr[newx][newy])
8869       {
8870 	AmoebaCnt2[AmoebaNr[newx][newy]]--;
8871 	if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8872 	    Feld[newx][newy] == EL_BD_AMOEBA)
8873 	  AmoebaCnt[AmoebaNr[newx][newy]]--;
8874       }
8875 
8876 #if 0
8877       /* !!! test !!! */
8878       if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8879       {
8880 	RemoveMovingField(newx, newy);
8881       }
8882 #else
8883       if (IS_MOVING(newx, newy))
8884       {
8885 	RemoveMovingField(newx, newy);
8886       }
8887 #endif
8888       else
8889       {
8890 	Feld[newx][newy] = EL_EMPTY;
8891 	TEST_DrawLevelField(newx, newy);
8892       }
8893 
8894       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8895     }
8896     else if ((element == EL_PACMAN || element == EL_MOLE)
8897 	     && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8898     {
8899       if (AmoebaNr[newx][newy])
8900       {
8901 	AmoebaCnt2[AmoebaNr[newx][newy]]--;
8902 	if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8903 	    Feld[newx][newy] == EL_BD_AMOEBA)
8904 	  AmoebaCnt[AmoebaNr[newx][newy]]--;
8905       }
8906 
8907       if (element == EL_MOLE)
8908       {
8909 	Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8910 	PlayLevelSound(x, y, SND_MOLE_DIGGING);
8911 
8912 	ResetGfxAnimation(x, y);
8913 	GfxAction[x][y] = ACTION_DIGGING;
8914 	TEST_DrawLevelField(x, y);
8915 
8916 	MovDelay[newx][newy] = 0;	/* start amoeba shrinking delay */
8917 
8918 	return;				/* wait for shrinking amoeba */
8919       }
8920       else	/* element == EL_PACMAN */
8921       {
8922 	Feld[newx][newy] = EL_EMPTY;
8923 	TEST_DrawLevelField(newx, newy);
8924 	PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8925       }
8926     }
8927     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8928 	     (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8929 	      (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8930     {
8931       /* wait for shrinking amoeba to completely disappear */
8932       return;
8933     }
8934     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8935     {
8936       /* object was running against a wall */
8937 
8938       TurnRound(x, y);
8939 
8940 #if 0
8941       /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8942       if (move_pattern & MV_ANY_DIRECTION &&
8943 	  move_pattern == MovDir[x][y])
8944       {
8945 	int blocking_element =
8946 	  (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8947 
8948 	CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8949 				 MovDir[x][y]);
8950 
8951 	element = Feld[x][y];	/* element might have changed */
8952       }
8953 #endif
8954 
8955       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8956 	DrawLevelElementAnimation(x, y, element);
8957 
8958       if (DONT_TOUCH(element))
8959 	TestIfBadThingTouchesPlayer(x, y);
8960 
8961       return;
8962     }
8963 
8964     InitMovingField(x, y, MovDir[x][y]);
8965 
8966     PlayLevelSoundAction(x, y, ACTION_MOVING);
8967   }
8968 
8969   if (MovDir[x][y])
8970     ContinueMoving(x, y);
8971 }
8972 
ContinueMoving(int x,int y)8973 void ContinueMoving(int x, int y)
8974 {
8975   int element = Feld[x][y];
8976   struct ElementInfo *ei = &element_info[element];
8977   int direction = MovDir[x][y];
8978   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8979   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8980   int newx = x + dx, newy = y + dy;
8981   int stored = Store[x][y];
8982   int stored_new = Store[newx][newy];
8983   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8984   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8985   boolean last_line = (newy == lev_fieldy - 1);
8986 
8987   MovPos[x][y] += getElementMoveStepsize(x, y);
8988 
8989   if (pushed_by_player)	/* special case: moving object pushed by player */
8990     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8991 
8992   if (ABS(MovPos[x][y]) < TILEX)
8993   {
8994 #if 0
8995     int ee = Feld[x][y];
8996     int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8997     int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8998 
8999     printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
9000 	   x, y, ABS(MovPos[x][y]),
9001 	   ee, gg, ff,
9002 	   GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
9003 #endif
9004 
9005     TEST_DrawLevelField(x, y);
9006 
9007     return;	/* element is still moving */
9008   }
9009 
9010   /* element reached destination field */
9011 
9012   Feld[x][y] = EL_EMPTY;
9013   Feld[newx][newy] = element;
9014   MovPos[x][y] = 0;	/* force "not moving" for "crumbled sand" */
9015 
9016   if (Store[x][y] == EL_ACID)	/* element is moving into acid pool */
9017   {
9018     element = Feld[newx][newy] = EL_ACID;
9019   }
9020   else if (element == EL_MOLE)
9021   {
9022     Feld[x][y] = EL_SAND;
9023 
9024     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9025   }
9026   else if (element == EL_QUICKSAND_FILLING)
9027   {
9028     element = Feld[newx][newy] = get_next_element(element);
9029     Store[newx][newy] = Store[x][y];
9030   }
9031   else if (element == EL_QUICKSAND_EMPTYING)
9032   {
9033     Feld[x][y] = get_next_element(element);
9034     element = Feld[newx][newy] = Store[x][y];
9035   }
9036   else if (element == EL_QUICKSAND_FAST_FILLING)
9037   {
9038     element = Feld[newx][newy] = get_next_element(element);
9039     Store[newx][newy] = Store[x][y];
9040   }
9041   else if (element == EL_QUICKSAND_FAST_EMPTYING)
9042   {
9043     Feld[x][y] = get_next_element(element);
9044     element = Feld[newx][newy] = Store[x][y];
9045   }
9046   else if (element == EL_MAGIC_WALL_FILLING)
9047   {
9048     element = Feld[newx][newy] = get_next_element(element);
9049     if (!game.magic_wall_active)
9050       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9051     Store[newx][newy] = Store[x][y];
9052   }
9053   else if (element == EL_MAGIC_WALL_EMPTYING)
9054   {
9055     Feld[x][y] = get_next_element(element);
9056     if (!game.magic_wall_active)
9057       Feld[x][y] = EL_MAGIC_WALL_DEAD;
9058     element = Feld[newx][newy] = Store[x][y];
9059 
9060 #if USE_NEW_CUSTOM_VALUE
9061     InitField(newx, newy, FALSE);
9062 #endif
9063   }
9064   else if (element == EL_BD_MAGIC_WALL_FILLING)
9065   {
9066     element = Feld[newx][newy] = get_next_element(element);
9067     if (!game.magic_wall_active)
9068       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9069     Store[newx][newy] = Store[x][y];
9070   }
9071   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9072   {
9073     Feld[x][y] = get_next_element(element);
9074     if (!game.magic_wall_active)
9075       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9076     element = Feld[newx][newy] = Store[x][y];
9077 
9078 #if USE_NEW_CUSTOM_VALUE
9079     InitField(newx, newy, FALSE);
9080 #endif
9081   }
9082   else if (element == EL_DC_MAGIC_WALL_FILLING)
9083   {
9084     element = Feld[newx][newy] = get_next_element(element);
9085     if (!game.magic_wall_active)
9086       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9087     Store[newx][newy] = Store[x][y];
9088   }
9089   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9090   {
9091     Feld[x][y] = get_next_element(element);
9092     if (!game.magic_wall_active)
9093       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9094     element = Feld[newx][newy] = Store[x][y];
9095 
9096 #if USE_NEW_CUSTOM_VALUE
9097     InitField(newx, newy, FALSE);
9098 #endif
9099   }
9100   else if (element == EL_AMOEBA_DROPPING)
9101   {
9102     Feld[x][y] = get_next_element(element);
9103     element = Feld[newx][newy] = Store[x][y];
9104   }
9105   else if (element == EL_SOKOBAN_OBJECT)
9106   {
9107     if (Back[x][y])
9108       Feld[x][y] = Back[x][y];
9109 
9110     if (Back[newx][newy])
9111       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9112 
9113     Back[x][y] = Back[newx][newy] = 0;
9114   }
9115 
9116   Store[x][y] = EL_EMPTY;
9117   MovPos[x][y] = 0;
9118   MovDir[x][y] = 0;
9119   MovDelay[x][y] = 0;
9120 
9121   MovDelay[newx][newy] = 0;
9122 
9123   if (CAN_CHANGE_OR_HAS_ACTION(element))
9124   {
9125     /* copy element change control values to new field */
9126     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9127     ChangePage[newx][newy]  = ChangePage[x][y];
9128     ChangeCount[newx][newy] = ChangeCount[x][y];
9129     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9130   }
9131 
9132 #if USE_NEW_CUSTOM_VALUE
9133   CustomValue[newx][newy] = CustomValue[x][y];
9134 #endif
9135 
9136   ChangeDelay[x][y] = 0;
9137   ChangePage[x][y] = -1;
9138   ChangeCount[x][y] = 0;
9139   ChangeEvent[x][y] = -1;
9140 
9141 #if USE_NEW_CUSTOM_VALUE
9142   CustomValue[x][y] = 0;
9143 #endif
9144 
9145   /* copy animation control values to new field */
9146   GfxFrame[newx][newy]  = GfxFrame[x][y];
9147   GfxRandom[newx][newy] = GfxRandom[x][y];	/* keep same random value */
9148   GfxAction[newx][newy] = GfxAction[x][y];	/* keep action one frame  */
9149   GfxDir[newx][newy]    = GfxDir[x][y];		/* keep element direction */
9150 
9151   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9152 
9153   /* some elements can leave other elements behind after moving */
9154 #if 1
9155   if (ei->move_leave_element != EL_EMPTY &&
9156       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9157       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9158 #else
9159   if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9160       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9161       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9162 #endif
9163   {
9164     int move_leave_element = ei->move_leave_element;
9165 
9166 #if 1
9167 #if 1
9168     /* this makes it possible to leave the removed element again */
9169     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9170       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9171 #else
9172     /* this makes it possible to leave the removed element again */
9173     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9174       move_leave_element = stored;
9175 #endif
9176 #else
9177     /* this makes it possible to leave the removed element again */
9178     if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9179         ei->move_leave_element == EL_TRIGGER_ELEMENT)
9180       move_leave_element = stored;
9181 #endif
9182 
9183     Feld[x][y] = move_leave_element;
9184 
9185     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9186       MovDir[x][y] = direction;
9187 
9188     InitField(x, y, FALSE);
9189 
9190     if (GFX_CRUMBLED(Feld[x][y]))
9191       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9192 
9193     if (ELEM_IS_PLAYER(move_leave_element))
9194       RelocatePlayer(x, y, move_leave_element);
9195   }
9196 
9197   /* do this after checking for left-behind element */
9198   ResetGfxAnimation(x, y);	/* reset animation values for old field */
9199 
9200   if (!CAN_MOVE(element) ||
9201       (CAN_FALL(element) && direction == MV_DOWN &&
9202        (element == EL_SPRING ||
9203 	element_info[element].move_pattern == MV_WHEN_PUSHED ||
9204 	element_info[element].move_pattern == MV_WHEN_DROPPED)))
9205     GfxDir[x][y] = MovDir[newx][newy] = 0;
9206 
9207   TEST_DrawLevelField(x, y);
9208   TEST_DrawLevelField(newx, newy);
9209 
9210   Stop[newx][newy] = TRUE;	/* ignore this element until the next frame */
9211 
9212   /* prevent pushed element from moving on in pushed direction */
9213   if (pushed_by_player && CAN_MOVE(element) &&
9214       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9215       !(element_info[element].move_pattern & direction))
9216     TurnRound(newx, newy);
9217 
9218   /* prevent elements on conveyor belt from moving on in last direction */
9219   if (pushed_by_conveyor && CAN_FALL(element) &&
9220       direction & MV_HORIZONTAL)
9221     MovDir[newx][newy] = 0;
9222 
9223   if (!pushed_by_player)
9224   {
9225     int nextx = newx + dx, nexty = newy + dy;
9226     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9227 
9228     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9229 
9230     if (CAN_FALL(element) && direction == MV_DOWN)
9231       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9232 
9233     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9234       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9235 
9236 #if USE_FIX_IMPACT_COLLISION
9237     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9238       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9239 #endif
9240   }
9241 
9242   if (DONT_TOUCH(element))	/* object may be nasty to player or others */
9243   {
9244     TestIfBadThingTouchesPlayer(newx, newy);
9245     TestIfBadThingTouchesFriend(newx, newy);
9246 
9247     if (!IS_CUSTOM_ELEMENT(element))
9248       TestIfBadThingTouchesOtherBadThing(newx, newy);
9249   }
9250   else if (element == EL_PENGUIN)
9251     TestIfFriendTouchesBadThing(newx, newy);
9252 
9253   if (DONT_GET_HIT_BY(element))
9254   {
9255     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9256   }
9257 
9258   /* give the player one last chance (one more frame) to move away */
9259   if (CAN_FALL(element) && direction == MV_DOWN &&
9260       (last_line || (!IS_FREE(x, newy + 1) &&
9261 		     (!IS_PLAYER(x, newy + 1) ||
9262 		      game.engine_version < VERSION_IDENT(3,1,1,0)))))
9263     Impact(x, newy);
9264 
9265   if (pushed_by_player && !game.use_change_when_pushing_bug)
9266   {
9267     int push_side = MV_DIR_OPPOSITE(direction);
9268     struct PlayerInfo *player = PLAYERINFO(x, y);
9269 
9270     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9271 			       player->index_bit, push_side);
9272     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9273 					player->index_bit, push_side);
9274   }
9275 
9276   if (element == EL_EMC_ANDROID && pushed_by_player)	/* make another move */
9277     MovDelay[newx][newy] = 1;
9278 
9279   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9280 
9281   TestIfElementTouchesCustomElement(x, y);	/* empty or new element */
9282 
9283 #if 0
9284   if (ChangePage[newx][newy] != -1)		/* delayed change */
9285   {
9286     int page = ChangePage[newx][newy];
9287     struct ElementChangeInfo *change = &ei->change_page[page];
9288 
9289     ChangePage[newx][newy] = -1;
9290 
9291     if (change->can_change)
9292     {
9293       if (ChangeElement(newx, newy, element, page))
9294       {
9295         if (change->post_change_function)
9296           change->post_change_function(newx, newy);
9297       }
9298     }
9299 
9300     if (change->has_action)
9301       ExecuteCustomElementAction(newx, newy, element, page);
9302   }
9303 #endif
9304 
9305   TestIfElementHitsCustomElement(newx, newy, direction);
9306   TestIfPlayerTouchesCustomElement(newx, newy);
9307   TestIfElementTouchesCustomElement(newx, newy);
9308 
9309   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9310       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9311     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9312 			     MV_DIR_OPPOSITE(direction));
9313 }
9314 
AmoebeNachbarNr(int ax,int ay)9315 int AmoebeNachbarNr(int ax, int ay)
9316 {
9317   int i;
9318   int element = Feld[ax][ay];
9319   int group_nr = 0;
9320   static int xy[4][2] =
9321   {
9322     { 0, -1 },
9323     { -1, 0 },
9324     { +1, 0 },
9325     { 0, +1 }
9326   };
9327 
9328   for (i = 0; i < NUM_DIRECTIONS; i++)
9329   {
9330     int x = ax + xy[i][0];
9331     int y = ay + xy[i][1];
9332 
9333     if (!IN_LEV_FIELD(x, y))
9334       continue;
9335 
9336     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9337       group_nr = AmoebaNr[x][y];
9338   }
9339 
9340   return group_nr;
9341 }
9342 
AmoebenVereinigen(int ax,int ay)9343 void AmoebenVereinigen(int ax, int ay)
9344 {
9345   int i, x, y, xx, yy;
9346   int new_group_nr = AmoebaNr[ax][ay];
9347   static int xy[4][2] =
9348   {
9349     { 0, -1 },
9350     { -1, 0 },
9351     { +1, 0 },
9352     { 0, +1 }
9353   };
9354 
9355   if (new_group_nr == 0)
9356     return;
9357 
9358   for (i = 0; i < NUM_DIRECTIONS; i++)
9359   {
9360     x = ax + xy[i][0];
9361     y = ay + xy[i][1];
9362 
9363     if (!IN_LEV_FIELD(x, y))
9364       continue;
9365 
9366     if ((Feld[x][y] == EL_AMOEBA_FULL ||
9367 	 Feld[x][y] == EL_BD_AMOEBA ||
9368 	 Feld[x][y] == EL_AMOEBA_DEAD) &&
9369 	AmoebaNr[x][y] != new_group_nr)
9370     {
9371       int old_group_nr = AmoebaNr[x][y];
9372 
9373       if (old_group_nr == 0)
9374 	return;
9375 
9376       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9377       AmoebaCnt[old_group_nr] = 0;
9378       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9379       AmoebaCnt2[old_group_nr] = 0;
9380 
9381       SCAN_PLAYFIELD(xx, yy)
9382       {
9383 	if (AmoebaNr[xx][yy] == old_group_nr)
9384 	  AmoebaNr[xx][yy] = new_group_nr;
9385       }
9386     }
9387   }
9388 }
9389 
AmoebeUmwandeln(int ax,int ay)9390 void AmoebeUmwandeln(int ax, int ay)
9391 {
9392   int i, x, y;
9393 
9394   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9395   {
9396     int group_nr = AmoebaNr[ax][ay];
9397 
9398 #ifdef DEBUG
9399     if (group_nr == 0)
9400     {
9401       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9402       printf("AmoebeUmwandeln(): This should never happen!\n");
9403       return;
9404     }
9405 #endif
9406 
9407     SCAN_PLAYFIELD(x, y)
9408     {
9409       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9410       {
9411 	AmoebaNr[x][y] = 0;
9412 	Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9413       }
9414     }
9415 
9416     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9417 			    SND_AMOEBA_TURNING_TO_GEM :
9418 			    SND_AMOEBA_TURNING_TO_ROCK));
9419     Bang(ax, ay);
9420   }
9421   else
9422   {
9423     static int xy[4][2] =
9424     {
9425       { 0, -1 },
9426       { -1, 0 },
9427       { +1, 0 },
9428       { 0, +1 }
9429     };
9430 
9431     for (i = 0; i < NUM_DIRECTIONS; i++)
9432     {
9433       x = ax + xy[i][0];
9434       y = ay + xy[i][1];
9435 
9436       if (!IN_LEV_FIELD(x, y))
9437 	continue;
9438 
9439       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9440       {
9441 	PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9442 			      SND_AMOEBA_TURNING_TO_GEM :
9443 			      SND_AMOEBA_TURNING_TO_ROCK));
9444 	Bang(x, y);
9445       }
9446     }
9447   }
9448 }
9449 
AmoebeUmwandelnBD(int ax,int ay,int new_element)9450 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9451 {
9452   int x, y;
9453   int group_nr = AmoebaNr[ax][ay];
9454   boolean done = FALSE;
9455 
9456 #ifdef DEBUG
9457   if (group_nr == 0)
9458   {
9459     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9460     printf("AmoebeUmwandelnBD(): This should never happen!\n");
9461     return;
9462   }
9463 #endif
9464 
9465   SCAN_PLAYFIELD(x, y)
9466   {
9467     if (AmoebaNr[x][y] == group_nr &&
9468 	(Feld[x][y] == EL_AMOEBA_DEAD ||
9469 	 Feld[x][y] == EL_BD_AMOEBA ||
9470 	 Feld[x][y] == EL_AMOEBA_GROWING))
9471     {
9472       AmoebaNr[x][y] = 0;
9473       Feld[x][y] = new_element;
9474       InitField(x, y, FALSE);
9475       TEST_DrawLevelField(x, y);
9476       done = TRUE;
9477     }
9478   }
9479 
9480   if (done)
9481     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9482 			    SND_BD_AMOEBA_TURNING_TO_ROCK :
9483 			    SND_BD_AMOEBA_TURNING_TO_GEM));
9484 }
9485 
AmoebeWaechst(int x,int y)9486 void AmoebeWaechst(int x, int y)
9487 {
9488   static unsigned long sound_delay = 0;
9489   static unsigned long sound_delay_value = 0;
9490 
9491   if (!MovDelay[x][y])		/* start new growing cycle */
9492   {
9493     MovDelay[x][y] = 7;
9494 
9495     if (DelayReached(&sound_delay, sound_delay_value))
9496     {
9497       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9498       sound_delay_value = 30;
9499     }
9500   }
9501 
9502   if (MovDelay[x][y])		/* wait some time before growing bigger */
9503   {
9504     MovDelay[x][y]--;
9505     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9506     {
9507       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9508 					   6 - MovDelay[x][y]);
9509 
9510       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9511     }
9512 
9513     if (!MovDelay[x][y])
9514     {
9515       Feld[x][y] = Store[x][y];
9516       Store[x][y] = 0;
9517       TEST_DrawLevelField(x, y);
9518     }
9519   }
9520 }
9521 
AmoebaDisappearing(int x,int y)9522 void AmoebaDisappearing(int x, int y)
9523 {
9524   static unsigned long sound_delay = 0;
9525   static unsigned long sound_delay_value = 0;
9526 
9527   if (!MovDelay[x][y])		/* start new shrinking cycle */
9528   {
9529     MovDelay[x][y] = 7;
9530 
9531     if (DelayReached(&sound_delay, sound_delay_value))
9532       sound_delay_value = 30;
9533   }
9534 
9535   if (MovDelay[x][y])		/* wait some time before shrinking */
9536   {
9537     MovDelay[x][y]--;
9538     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9539     {
9540       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9541 					   6 - MovDelay[x][y]);
9542 
9543       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9544     }
9545 
9546     if (!MovDelay[x][y])
9547     {
9548       Feld[x][y] = EL_EMPTY;
9549       TEST_DrawLevelField(x, y);
9550 
9551       /* don't let mole enter this field in this cycle;
9552 	 (give priority to objects falling to this field from above) */
9553       Stop[x][y] = TRUE;
9554     }
9555   }
9556 }
9557 
AmoebeAbleger(int ax,int ay)9558 void AmoebeAbleger(int ax, int ay)
9559 {
9560   int i;
9561   int element = Feld[ax][ay];
9562   int graphic = el2img(element);
9563   int newax = ax, neway = ay;
9564   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9565   static int xy[4][2] =
9566   {
9567     { 0, -1 },
9568     { -1, 0 },
9569     { +1, 0 },
9570     { 0, +1 }
9571   };
9572 
9573   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9574   {
9575     Feld[ax][ay] = EL_AMOEBA_DEAD;
9576     TEST_DrawLevelField(ax, ay);
9577     return;
9578   }
9579 
9580   if (IS_ANIMATED(graphic))
9581     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9582 
9583   if (!MovDelay[ax][ay])	/* start making new amoeba field */
9584     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9585 
9586   if (MovDelay[ax][ay])		/* wait some time before making new amoeba */
9587   {
9588     MovDelay[ax][ay]--;
9589     if (MovDelay[ax][ay])
9590       return;
9591   }
9592 
9593   if (can_drop)			/* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9594   {
9595     int start = RND(4);
9596     int x = ax + xy[start][0];
9597     int y = ay + xy[start][1];
9598 
9599     if (!IN_LEV_FIELD(x, y))
9600       return;
9601 
9602     if (IS_FREE(x, y) ||
9603 	CAN_GROW_INTO(Feld[x][y]) ||
9604 	Feld[x][y] == EL_QUICKSAND_EMPTY ||
9605 	Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9606     {
9607       newax = x;
9608       neway = y;
9609     }
9610 
9611     if (newax == ax && neway == ay)
9612       return;
9613   }
9614   else				/* normal or "filled" (BD style) amoeba */
9615   {
9616     int start = RND(4);
9617     boolean waiting_for_player = FALSE;
9618 
9619     for (i = 0; i < NUM_DIRECTIONS; i++)
9620     {
9621       int j = (start + i) % 4;
9622       int x = ax + xy[j][0];
9623       int y = ay + xy[j][1];
9624 
9625       if (!IN_LEV_FIELD(x, y))
9626 	continue;
9627 
9628       if (IS_FREE(x, y) ||
9629 	  CAN_GROW_INTO(Feld[x][y]) ||
9630 	  Feld[x][y] == EL_QUICKSAND_EMPTY ||
9631 	  Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9632       {
9633 	newax = x;
9634 	neway = y;
9635 	break;
9636       }
9637       else if (IS_PLAYER(x, y))
9638 	waiting_for_player = TRUE;
9639     }
9640 
9641     if (newax == ax && neway == ay)		/* amoeba cannot grow */
9642     {
9643       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9644       {
9645 	Feld[ax][ay] = EL_AMOEBA_DEAD;
9646 	TEST_DrawLevelField(ax, ay);
9647 	AmoebaCnt[AmoebaNr[ax][ay]]--;
9648 
9649 	if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)	/* amoeba is completely dead */
9650 	{
9651 	  if (element == EL_AMOEBA_FULL)
9652 	    AmoebeUmwandeln(ax, ay);
9653 	  else if (element == EL_BD_AMOEBA)
9654 	    AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9655 	}
9656       }
9657       return;
9658     }
9659     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9660     {
9661       /* amoeba gets larger by growing in some direction */
9662 
9663       int new_group_nr = AmoebaNr[ax][ay];
9664 
9665 #ifdef DEBUG
9666   if (new_group_nr == 0)
9667   {
9668     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9669     printf("AmoebeAbleger(): This should never happen!\n");
9670     return;
9671   }
9672 #endif
9673 
9674       AmoebaNr[newax][neway] = new_group_nr;
9675       AmoebaCnt[new_group_nr]++;
9676       AmoebaCnt2[new_group_nr]++;
9677 
9678       /* if amoeba touches other amoeba(s) after growing, unify them */
9679       AmoebenVereinigen(newax, neway);
9680 
9681       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9682       {
9683 	AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9684 	return;
9685       }
9686     }
9687   }
9688 
9689   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9690       (neway == lev_fieldy - 1 && newax != ax))
9691   {
9692     Feld[newax][neway] = EL_AMOEBA_GROWING;	/* creation of new amoeba */
9693     Store[newax][neway] = element;
9694   }
9695   else if (neway == ay || element == EL_EMC_DRIPPER)
9696   {
9697     Feld[newax][neway] = EL_AMOEBA_DROP;	/* drop left/right of amoeba */
9698 
9699     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9700   }
9701   else
9702   {
9703     InitMovingField(ax, ay, MV_DOWN);		/* drop dripping from amoeba */
9704     Feld[ax][ay] = EL_AMOEBA_DROPPING;
9705     Store[ax][ay] = EL_AMOEBA_DROP;
9706     ContinueMoving(ax, ay);
9707     return;
9708   }
9709 
9710   TEST_DrawLevelField(newax, neway);
9711 }
9712 
Life(int ax,int ay)9713 void Life(int ax, int ay)
9714 {
9715   int x1, y1, x2, y2;
9716   int life_time = 40;
9717   int element = Feld[ax][ay];
9718   int graphic = el2img(element);
9719   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9720 			 level.biomaze);
9721   boolean changed = FALSE;
9722 
9723   if (IS_ANIMATED(graphic))
9724     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9725 
9726   if (Stop[ax][ay])
9727     return;
9728 
9729   if (!MovDelay[ax][ay])	/* start new "game of life" cycle */
9730     MovDelay[ax][ay] = life_time;
9731 
9732   if (MovDelay[ax][ay])		/* wait some time before next cycle */
9733   {
9734     MovDelay[ax][ay]--;
9735     if (MovDelay[ax][ay])
9736       return;
9737   }
9738 
9739   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9740   {
9741     int xx = ax+x1, yy = ay+y1;
9742     int nachbarn = 0;
9743 
9744     if (!IN_LEV_FIELD(xx, yy))
9745       continue;
9746 
9747     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9748     {
9749       int x = xx+x2, y = yy+y2;
9750 
9751       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9752 	continue;
9753 
9754       if (((Feld[x][y] == element ||
9755 	    (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9756 	   !Stop[x][y]) ||
9757 	  (IS_FREE(x, y) && Stop[x][y]))
9758 	nachbarn++;
9759     }
9760 
9761     if (xx == ax && yy == ay)		/* field in the middle */
9762     {
9763       if (nachbarn < life_parameter[0] ||
9764 	  nachbarn > life_parameter[1])
9765       {
9766 	Feld[xx][yy] = EL_EMPTY;
9767 	if (!Stop[xx][yy])
9768 	  TEST_DrawLevelField(xx, yy);
9769 	Stop[xx][yy] = TRUE;
9770 	changed = TRUE;
9771       }
9772     }
9773     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9774     {					/* free border field */
9775       if (nachbarn >= life_parameter[2] &&
9776 	  nachbarn <= life_parameter[3])
9777       {
9778 	Feld[xx][yy] = element;
9779 	MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9780 	if (!Stop[xx][yy])
9781 	  TEST_DrawLevelField(xx, yy);
9782 	Stop[xx][yy] = TRUE;
9783 	changed = TRUE;
9784       }
9785     }
9786   }
9787 
9788   if (changed)
9789     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9790 		   SND_GAME_OF_LIFE_GROWING);
9791 }
9792 
InitRobotWheel(int x,int y)9793 static void InitRobotWheel(int x, int y)
9794 {
9795   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9796 }
9797 
RunRobotWheel(int x,int y)9798 static void RunRobotWheel(int x, int y)
9799 {
9800   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9801 }
9802 
StopRobotWheel(int x,int y)9803 static void StopRobotWheel(int x, int y)
9804 {
9805   if (ZX == x && ZY == y)
9806   {
9807     ZX = ZY = -1;
9808 
9809     game.robot_wheel_active = FALSE;
9810   }
9811 }
9812 
InitTimegateWheel(int x,int y)9813 static void InitTimegateWheel(int x, int y)
9814 {
9815   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9816 }
9817 
RunTimegateWheel(int x,int y)9818 static void RunTimegateWheel(int x, int y)
9819 {
9820   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9821 }
9822 
InitMagicBallDelay(int x,int y)9823 static void InitMagicBallDelay(int x, int y)
9824 {
9825 #if 1
9826   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9827 #else
9828   ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9829 #endif
9830 }
9831 
ActivateMagicBall(int bx,int by)9832 static void ActivateMagicBall(int bx, int by)
9833 {
9834   int x, y;
9835 
9836   if (level.ball_random)
9837   {
9838     int pos_border = RND(8);	/* select one of the eight border elements */
9839     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9840     int xx = pos_content % 3;
9841     int yy = pos_content / 3;
9842 
9843     x = bx - 1 + xx;
9844     y = by - 1 + yy;
9845 
9846     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9847       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9848   }
9849   else
9850   {
9851     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9852     {
9853       int xx = x - bx + 1;
9854       int yy = y - by + 1;
9855 
9856       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9857 	CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9858     }
9859   }
9860 
9861   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9862 }
9863 
CheckExit(int x,int y)9864 void CheckExit(int x, int y)
9865 {
9866   if (local_player->gems_still_needed > 0 ||
9867       local_player->sokobanfields_still_needed > 0 ||
9868       local_player->lights_still_needed > 0)
9869   {
9870     int element = Feld[x][y];
9871     int graphic = el2img(element);
9872 
9873     if (IS_ANIMATED(graphic))
9874       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9875 
9876     return;
9877   }
9878 
9879   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9880     return;
9881 
9882   Feld[x][y] = EL_EXIT_OPENING;
9883 
9884   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9885 }
9886 
CheckExitEM(int x,int y)9887 void CheckExitEM(int x, int y)
9888 {
9889   if (local_player->gems_still_needed > 0 ||
9890       local_player->sokobanfields_still_needed > 0 ||
9891       local_player->lights_still_needed > 0)
9892   {
9893     int element = Feld[x][y];
9894     int graphic = el2img(element);
9895 
9896     if (IS_ANIMATED(graphic))
9897       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9898 
9899     return;
9900   }
9901 
9902   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9903     return;
9904 
9905   Feld[x][y] = EL_EM_EXIT_OPENING;
9906 
9907   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9908 }
9909 
CheckExitSteel(int x,int y)9910 void CheckExitSteel(int x, int y)
9911 {
9912   if (local_player->gems_still_needed > 0 ||
9913       local_player->sokobanfields_still_needed > 0 ||
9914       local_player->lights_still_needed > 0)
9915   {
9916     int element = Feld[x][y];
9917     int graphic = el2img(element);
9918 
9919     if (IS_ANIMATED(graphic))
9920       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9921 
9922     return;
9923   }
9924 
9925   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9926     return;
9927 
9928   Feld[x][y] = EL_STEEL_EXIT_OPENING;
9929 
9930   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9931 }
9932 
CheckExitSteelEM(int x,int y)9933 void CheckExitSteelEM(int x, int y)
9934 {
9935   if (local_player->gems_still_needed > 0 ||
9936       local_player->sokobanfields_still_needed > 0 ||
9937       local_player->lights_still_needed > 0)
9938   {
9939     int element = Feld[x][y];
9940     int graphic = el2img(element);
9941 
9942     if (IS_ANIMATED(graphic))
9943       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9944 
9945     return;
9946   }
9947 
9948   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9949     return;
9950 
9951   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9952 
9953   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9954 }
9955 
CheckExitSP(int x,int y)9956 void CheckExitSP(int x, int y)
9957 {
9958   if (local_player->gems_still_needed > 0)
9959   {
9960     int element = Feld[x][y];
9961     int graphic = el2img(element);
9962 
9963     if (IS_ANIMATED(graphic))
9964       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9965 
9966     return;
9967   }
9968 
9969   if (AllPlayersGone)	/* do not re-open exit door closed after last player */
9970     return;
9971 
9972   Feld[x][y] = EL_SP_EXIT_OPENING;
9973 
9974   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9975 }
9976 
CloseAllOpenTimegates()9977 static void CloseAllOpenTimegates()
9978 {
9979   int x, y;
9980 
9981   SCAN_PLAYFIELD(x, y)
9982   {
9983     int element = Feld[x][y];
9984 
9985     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9986     {
9987       Feld[x][y] = EL_TIMEGATE_CLOSING;
9988 
9989       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9990     }
9991   }
9992 }
9993 
DrawTwinkleOnField(int x,int y)9994 void DrawTwinkleOnField(int x, int y)
9995 {
9996   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9997     return;
9998 
9999   if (Feld[x][y] == EL_BD_DIAMOND)
10000     return;
10001 
10002   if (MovDelay[x][y] == 0)	/* next animation frame */
10003     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
10004 
10005   if (MovDelay[x][y] != 0)	/* wait some time before next frame */
10006   {
10007     MovDelay[x][y]--;
10008 
10009     DrawLevelElementAnimation(x, y, Feld[x][y]);
10010 
10011     if (MovDelay[x][y] != 0)
10012     {
10013       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
10014 					   10 - MovDelay[x][y]);
10015 
10016       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
10017     }
10018   }
10019 }
10020 
MauerWaechst(int x,int y)10021 void MauerWaechst(int x, int y)
10022 {
10023   int delay = 6;
10024 
10025   if (!MovDelay[x][y])		/* next animation frame */
10026     MovDelay[x][y] = 3 * delay;
10027 
10028   if (MovDelay[x][y])		/* wait some time before next frame */
10029   {
10030     MovDelay[x][y]--;
10031 
10032     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
10033     {
10034       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
10035       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
10036 
10037       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
10038     }
10039 
10040     if (!MovDelay[x][y])
10041     {
10042       if (MovDir[x][y] == MV_LEFT)
10043       {
10044 	if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
10045 	  TEST_DrawLevelField(x - 1, y);
10046       }
10047       else if (MovDir[x][y] == MV_RIGHT)
10048       {
10049 	if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
10050 	  TEST_DrawLevelField(x + 1, y);
10051       }
10052       else if (MovDir[x][y] == MV_UP)
10053       {
10054 	if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10055 	  TEST_DrawLevelField(x, y - 1);
10056       }
10057       else
10058       {
10059 	if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10060 	  TEST_DrawLevelField(x, y + 1);
10061       }
10062 
10063       Feld[x][y] = Store[x][y];
10064       Store[x][y] = 0;
10065       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10066       TEST_DrawLevelField(x, y);
10067     }
10068   }
10069 }
10070 
MauerAbleger(int ax,int ay)10071 void MauerAbleger(int ax, int ay)
10072 {
10073   int element = Feld[ax][ay];
10074   int graphic = el2img(element);
10075   boolean oben_frei = FALSE, unten_frei = FALSE;
10076   boolean links_frei = FALSE, rechts_frei = FALSE;
10077   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10078   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10079   boolean new_wall = FALSE;
10080 
10081   if (IS_ANIMATED(graphic))
10082     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10083 
10084   if (!MovDelay[ax][ay])	/* start building new wall */
10085     MovDelay[ax][ay] = 6;
10086 
10087   if (MovDelay[ax][ay])		/* wait some time before building new wall */
10088   {
10089     MovDelay[ax][ay]--;
10090     if (MovDelay[ax][ay])
10091       return;
10092   }
10093 
10094   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10095     oben_frei = TRUE;
10096   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10097     unten_frei = TRUE;
10098   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10099     links_frei = TRUE;
10100   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10101     rechts_frei = TRUE;
10102 
10103   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10104       element == EL_EXPANDABLE_WALL_ANY)
10105   {
10106     if (oben_frei)
10107     {
10108       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10109       Store[ax][ay-1] = element;
10110       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10111       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10112   	DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10113 		    IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10114       new_wall = TRUE;
10115     }
10116     if (unten_frei)
10117     {
10118       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10119       Store[ax][ay+1] = element;
10120       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10121       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10122   	DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10123 		    IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10124       new_wall = TRUE;
10125     }
10126   }
10127 
10128   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10129       element == EL_EXPANDABLE_WALL_ANY ||
10130       element == EL_EXPANDABLE_WALL ||
10131       element == EL_BD_EXPANDABLE_WALL)
10132   {
10133     if (links_frei)
10134     {
10135       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10136       Store[ax-1][ay] = element;
10137       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10138       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10139   	DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10140 		    IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10141       new_wall = TRUE;
10142     }
10143 
10144     if (rechts_frei)
10145     {
10146       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10147       Store[ax+1][ay] = element;
10148       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10149       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10150   	DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10151 		    IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10152       new_wall = TRUE;
10153     }
10154   }
10155 
10156   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10157     TEST_DrawLevelField(ax, ay);
10158 
10159   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10160     oben_massiv = TRUE;
10161   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10162     unten_massiv = TRUE;
10163   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10164     links_massiv = TRUE;
10165   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10166     rechts_massiv = TRUE;
10167 
10168   if (((oben_massiv && unten_massiv) ||
10169        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10170        element == EL_EXPANDABLE_WALL) &&
10171       ((links_massiv && rechts_massiv) ||
10172        element == EL_EXPANDABLE_WALL_VERTICAL))
10173     Feld[ax][ay] = EL_WALL;
10174 
10175   if (new_wall)
10176     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10177 }
10178 
MauerAblegerStahl(int ax,int ay)10179 void MauerAblegerStahl(int ax, int ay)
10180 {
10181   int element = Feld[ax][ay];
10182   int graphic = el2img(element);
10183   boolean oben_frei = FALSE, unten_frei = FALSE;
10184   boolean links_frei = FALSE, rechts_frei = FALSE;
10185   boolean oben_massiv = FALSE, unten_massiv = FALSE;
10186   boolean links_massiv = FALSE, rechts_massiv = FALSE;
10187   boolean new_wall = FALSE;
10188 
10189   if (IS_ANIMATED(graphic))
10190     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10191 
10192   if (!MovDelay[ax][ay])	/* start building new wall */
10193     MovDelay[ax][ay] = 6;
10194 
10195   if (MovDelay[ax][ay])		/* wait some time before building new wall */
10196   {
10197     MovDelay[ax][ay]--;
10198     if (MovDelay[ax][ay])
10199       return;
10200   }
10201 
10202   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10203     oben_frei = TRUE;
10204   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10205     unten_frei = TRUE;
10206   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10207     links_frei = TRUE;
10208   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10209     rechts_frei = TRUE;
10210 
10211   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10212       element == EL_EXPANDABLE_STEELWALL_ANY)
10213   {
10214     if (oben_frei)
10215     {
10216       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10217       Store[ax][ay-1] = element;
10218       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10219       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10220   	DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10221 		    IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10222       new_wall = TRUE;
10223     }
10224     if (unten_frei)
10225     {
10226       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10227       Store[ax][ay+1] = element;
10228       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10229       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10230   	DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10231 		    IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10232       new_wall = TRUE;
10233     }
10234   }
10235 
10236   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10237       element == EL_EXPANDABLE_STEELWALL_ANY)
10238   {
10239     if (links_frei)
10240     {
10241       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10242       Store[ax-1][ay] = element;
10243       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10244       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10245   	DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10246 		    IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10247       new_wall = TRUE;
10248     }
10249 
10250     if (rechts_frei)
10251     {
10252       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10253       Store[ax+1][ay] = element;
10254       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10255       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10256   	DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10257 		    IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10258       new_wall = TRUE;
10259     }
10260   }
10261 
10262   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10263     oben_massiv = TRUE;
10264   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10265     unten_massiv = TRUE;
10266   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10267     links_massiv = TRUE;
10268   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10269     rechts_massiv = TRUE;
10270 
10271   if (((oben_massiv && unten_massiv) ||
10272        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10273       ((links_massiv && rechts_massiv) ||
10274        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10275     Feld[ax][ay] = EL_STEELWALL;
10276 
10277   if (new_wall)
10278     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10279 }
10280 
CheckForDragon(int x,int y)10281 void CheckForDragon(int x, int y)
10282 {
10283   int i, j;
10284   boolean dragon_found = FALSE;
10285   static int xy[4][2] =
10286   {
10287     { 0, -1 },
10288     { -1, 0 },
10289     { +1, 0 },
10290     { 0, +1 }
10291   };
10292 
10293   for (i = 0; i < NUM_DIRECTIONS; i++)
10294   {
10295     for (j = 0; j < 4; j++)
10296     {
10297       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10298 
10299       if (IN_LEV_FIELD(xx, yy) &&
10300 	  (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10301       {
10302 	if (Feld[xx][yy] == EL_DRAGON)
10303 	  dragon_found = TRUE;
10304       }
10305       else
10306 	break;
10307     }
10308   }
10309 
10310   if (!dragon_found)
10311   {
10312     for (i = 0; i < NUM_DIRECTIONS; i++)
10313     {
10314       for (j = 0; j < 3; j++)
10315       {
10316   	int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10317 
10318   	if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10319   	{
10320 	  Feld[xx][yy] = EL_EMPTY;
10321 	  TEST_DrawLevelField(xx, yy);
10322   	}
10323   	else
10324   	  break;
10325       }
10326     }
10327   }
10328 }
10329 
InitBuggyBase(int x,int y)10330 static void InitBuggyBase(int x, int y)
10331 {
10332   int element = Feld[x][y];
10333   int activating_delay = FRAMES_PER_SECOND / 4;
10334 
10335   ChangeDelay[x][y] =
10336     (element == EL_SP_BUGGY_BASE ?
10337      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10338      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10339      activating_delay :
10340      element == EL_SP_BUGGY_BASE_ACTIVE ?
10341      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10342 }
10343 
WarnBuggyBase(int x,int y)10344 static void WarnBuggyBase(int x, int y)
10345 {
10346   int i;
10347   static int xy[4][2] =
10348   {
10349     { 0, -1 },
10350     { -1, 0 },
10351     { +1, 0 },
10352     { 0, +1 }
10353   };
10354 
10355   for (i = 0; i < NUM_DIRECTIONS; i++)
10356   {
10357     int xx = x + xy[i][0];
10358     int yy = y + xy[i][1];
10359 
10360     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10361     {
10362       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10363 
10364       break;
10365     }
10366   }
10367 }
10368 
InitTrap(int x,int y)10369 static void InitTrap(int x, int y)
10370 {
10371   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10372 }
10373 
ActivateTrap(int x,int y)10374 static void ActivateTrap(int x, int y)
10375 {
10376   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10377 }
10378 
ChangeActiveTrap(int x,int y)10379 static void ChangeActiveTrap(int x, int y)
10380 {
10381   int graphic = IMG_TRAP_ACTIVE;
10382 
10383   /* if new animation frame was drawn, correct crumbled sand border */
10384   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10385     TEST_DrawLevelFieldCrumbled(x, y);
10386 }
10387 
getSpecialActionElement(int element,int number,int base_element)10388 static int getSpecialActionElement(int element, int number, int base_element)
10389 {
10390   return (element != EL_EMPTY ? element :
10391 	  number != -1 ? base_element + number - 1 :
10392 	  EL_EMPTY);
10393 }
10394 
getModifiedActionNumber(int value_old,int operator,int operand,int value_min,int value_max)10395 static int getModifiedActionNumber(int value_old, int operator, int operand,
10396 				   int value_min, int value_max)
10397 {
10398   int value_new = (operator == CA_MODE_SET      ? operand :
10399 		   operator == CA_MODE_ADD      ? value_old + operand :
10400 		   operator == CA_MODE_SUBTRACT ? value_old - operand :
10401 		   operator == CA_MODE_MULTIPLY ? value_old * operand :
10402 		   operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10403 		   operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10404 		   value_old);
10405 
10406   return (value_new < value_min ? value_min :
10407 	  value_new > value_max ? value_max :
10408 	  value_new);
10409 }
10410 
ExecuteCustomElementAction(int x,int y,int element,int page)10411 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10412 {
10413   struct ElementInfo *ei = &element_info[element];
10414   struct ElementChangeInfo *change = &ei->change_page[page];
10415   int target_element = change->target_element;
10416   int action_type = change->action_type;
10417   int action_mode = change->action_mode;
10418   int action_arg = change->action_arg;
10419   int action_element = change->action_element;
10420   int i;
10421 
10422   if (!change->has_action)
10423     return;
10424 
10425   /* ---------- determine action paramater values -------------------------- */
10426 
10427   int level_time_value =
10428     (level.time > 0 ? TimeLeft :
10429      TimePlayed);
10430 
10431   int action_arg_element_raw =
10432     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10433      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10434      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10435      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10436      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10437      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10438      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10439      EL_EMPTY);
10440   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10441 
10442 #if 0
10443   if (action_arg_element_raw == EL_GROUP_START)
10444     printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10445 #endif
10446 
10447   int action_arg_direction =
10448     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10449      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10450      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10451      change->actual_trigger_side :
10452      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10453      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10454      MV_NONE);
10455 
10456   int action_arg_number_min =
10457     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10458      CA_ARG_MIN);
10459 
10460   int action_arg_number_max =
10461     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10462      action_type == CA_SET_LEVEL_GEMS ? 999 :
10463      action_type == CA_SET_LEVEL_TIME ? 9999 :
10464      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10465      action_type == CA_SET_CE_VALUE ? 9999 :
10466      action_type == CA_SET_CE_SCORE ? 9999 :
10467      CA_ARG_MAX);
10468 
10469   int action_arg_number_reset =
10470     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10471      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10472      action_type == CA_SET_LEVEL_TIME ? level.time :
10473      action_type == CA_SET_LEVEL_SCORE ? 0 :
10474 #if USE_NEW_CUSTOM_VALUE
10475      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10476 #else
10477      action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10478 #endif
10479      action_type == CA_SET_CE_SCORE ? 0 :
10480      0);
10481 
10482   int action_arg_number =
10483     (action_arg <= CA_ARG_MAX ? action_arg :
10484      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10485      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10486      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10487      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10488      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10489      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10490 #if USE_NEW_CUSTOM_VALUE
10491      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10492 #else
10493      action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10494 #endif
10495      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10496      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10497      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10498      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10499      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10500      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10501      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10502      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10503      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10504      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10505      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10506      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10507      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10508      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10509      -1);
10510 
10511   int action_arg_number_old =
10512     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10513      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10514      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10515      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10516      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10517      0);
10518 
10519   int action_arg_number_new =
10520     getModifiedActionNumber(action_arg_number_old,
10521 			    action_mode, action_arg_number,
10522 			    action_arg_number_min, action_arg_number_max);
10523 
10524 #if 1
10525   int trigger_player_bits =
10526     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10527      change->actual_trigger_player_bits : change->trigger_player);
10528 #else
10529   int trigger_player_bits =
10530     (change->actual_trigger_player >= EL_PLAYER_1 &&
10531      change->actual_trigger_player <= EL_PLAYER_4 ?
10532      (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10533      PLAYER_BITS_ANY);
10534 #endif
10535 
10536   int action_arg_player_bits =
10537     (action_arg >= CA_ARG_PLAYER_1 &&
10538      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10539      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10540      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10541      PLAYER_BITS_ANY);
10542 
10543   /* ---------- execute action  -------------------------------------------- */
10544 
10545   switch (action_type)
10546   {
10547     case CA_NO_ACTION:
10548     {
10549       return;
10550     }
10551 
10552     /* ---------- level actions  ------------------------------------------- */
10553 
10554     case CA_RESTART_LEVEL:
10555     {
10556       game.restart_level = TRUE;
10557 
10558       break;
10559     }
10560 
10561     case CA_SHOW_ENVELOPE:
10562     {
10563       int element = getSpecialActionElement(action_arg_element,
10564 					    action_arg_number, EL_ENVELOPE_1);
10565 
10566       if (IS_ENVELOPE(element))
10567 	local_player->show_envelope = element;
10568 
10569       break;
10570     }
10571 
10572     case CA_SET_LEVEL_TIME:
10573     {
10574       if (level.time > 0)	/* only modify limited time value */
10575       {
10576 	TimeLeft = action_arg_number_new;
10577 
10578 #if 1
10579 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10580 
10581 	DisplayGameControlValues();
10582 #else
10583 	DrawGameValue_Time(TimeLeft);
10584 #endif
10585 
10586 	if (!TimeLeft && setup.time_limit)
10587 	  for (i = 0; i < MAX_PLAYERS; i++)
10588 	    KillPlayer(&stored_player[i]);
10589       }
10590 
10591       break;
10592     }
10593 
10594     case CA_SET_LEVEL_SCORE:
10595     {
10596       local_player->score = action_arg_number_new;
10597 
10598 #if 1
10599       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10600 
10601       DisplayGameControlValues();
10602 #else
10603       DrawGameValue_Score(local_player->score);
10604 #endif
10605 
10606       break;
10607     }
10608 
10609     case CA_SET_LEVEL_GEMS:
10610     {
10611       local_player->gems_still_needed = action_arg_number_new;
10612 
10613 #if 1
10614       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10615 
10616       DisplayGameControlValues();
10617 #else
10618       DrawGameValue_Emeralds(local_player->gems_still_needed);
10619 #endif
10620 
10621       break;
10622     }
10623 
10624 #if !USE_PLAYER_GRAVITY
10625     case CA_SET_LEVEL_GRAVITY:
10626     {
10627       game.gravity = (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE         :
10628 		      action_arg == CA_ARG_GRAVITY_ON     ? TRUE          :
10629 		      action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10630 		      game.gravity);
10631       break;
10632     }
10633 #endif
10634 
10635     case CA_SET_LEVEL_WIND:
10636     {
10637       game.wind_direction = action_arg_direction;
10638 
10639       break;
10640     }
10641 
10642     case CA_SET_LEVEL_RANDOM_SEED:
10643     {
10644 #if 1
10645       /* ensure that setting a new random seed while playing is predictable */
10646       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10647 #else
10648       InitRND(action_arg_number_new);
10649 #endif
10650 
10651 #if 0
10652       printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10653 #endif
10654 
10655 #if 0
10656       {
10657 	int i;
10658 
10659 	printf("::: ");
10660 	for (i = 0; i < 9; i++)
10661 	  printf("%d, ", RND(2));
10662 	printf("\n");
10663       }
10664 #endif
10665 
10666       break;
10667     }
10668 
10669     /* ---------- player actions  ------------------------------------------ */
10670 
10671     case CA_MOVE_PLAYER:
10672     {
10673       /* automatically move to the next field in specified direction */
10674       for (i = 0; i < MAX_PLAYERS; i++)
10675 	if (trigger_player_bits & (1 << i))
10676 	  stored_player[i].programmed_action = action_arg_direction;
10677 
10678       break;
10679     }
10680 
10681     case CA_EXIT_PLAYER:
10682     {
10683       for (i = 0; i < MAX_PLAYERS; i++)
10684 	if (action_arg_player_bits & (1 << i))
10685 	  PlayerWins(&stored_player[i]);
10686 
10687       break;
10688     }
10689 
10690     case CA_KILL_PLAYER:
10691     {
10692       for (i = 0; i < MAX_PLAYERS; i++)
10693 	if (action_arg_player_bits & (1 << i))
10694 	  KillPlayer(&stored_player[i]);
10695 
10696       break;
10697     }
10698 
10699     case CA_SET_PLAYER_KEYS:
10700     {
10701       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10702       int element = getSpecialActionElement(action_arg_element,
10703 					    action_arg_number, EL_KEY_1);
10704 
10705       if (IS_KEY(element))
10706       {
10707 	for (i = 0; i < MAX_PLAYERS; i++)
10708 	{
10709 	  if (trigger_player_bits & (1 << i))
10710 	  {
10711 	    stored_player[i].key[KEY_NR(element)] = key_state;
10712 
10713 	    DrawGameDoorValues();
10714 	  }
10715 	}
10716       }
10717 
10718       break;
10719     }
10720 
10721     case CA_SET_PLAYER_SPEED:
10722     {
10723 #if 0
10724       printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10725 #endif
10726 
10727       for (i = 0; i < MAX_PLAYERS; i++)
10728       {
10729 	if (trigger_player_bits & (1 << i))
10730 	{
10731 	  int move_stepsize = TILEX / stored_player[i].move_delay_value;
10732 
10733 	  if (action_arg == CA_ARG_SPEED_FASTER &&
10734 	      stored_player[i].cannot_move)
10735 	  {
10736 	    action_arg_number = STEPSIZE_VERY_SLOW;
10737 	  }
10738 	  else if (action_arg == CA_ARG_SPEED_SLOWER ||
10739 		   action_arg == CA_ARG_SPEED_FASTER)
10740 	  {
10741 	    action_arg_number = 2;
10742 	    action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10743 			   CA_MODE_MULTIPLY);
10744 	  }
10745 	  else if (action_arg == CA_ARG_NUMBER_RESET)
10746 	  {
10747 	    action_arg_number = level.initial_player_stepsize[i];
10748 	  }
10749 
10750 	  move_stepsize =
10751 	    getModifiedActionNumber(move_stepsize,
10752 				    action_mode,
10753 				    action_arg_number,
10754 				    action_arg_number_min,
10755 				    action_arg_number_max);
10756 
10757 	  SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10758 	}
10759       }
10760 
10761       break;
10762     }
10763 
10764     case CA_SET_PLAYER_SHIELD:
10765     {
10766       for (i = 0; i < MAX_PLAYERS; i++)
10767       {
10768 	if (trigger_player_bits & (1 << i))
10769 	{
10770 	  if (action_arg == CA_ARG_SHIELD_OFF)
10771 	  {
10772 	    stored_player[i].shield_normal_time_left = 0;
10773 	    stored_player[i].shield_deadly_time_left = 0;
10774 	  }
10775 	  else if (action_arg == CA_ARG_SHIELD_NORMAL)
10776 	  {
10777 	    stored_player[i].shield_normal_time_left = 999999;
10778 	  }
10779 	  else if (action_arg == CA_ARG_SHIELD_DEADLY)
10780 	  {
10781 	    stored_player[i].shield_normal_time_left = 999999;
10782 	    stored_player[i].shield_deadly_time_left = 999999;
10783 	  }
10784 	}
10785       }
10786 
10787       break;
10788     }
10789 
10790 #if USE_PLAYER_GRAVITY
10791     case CA_SET_PLAYER_GRAVITY:
10792     {
10793       for (i = 0; i < MAX_PLAYERS; i++)
10794       {
10795 	if (trigger_player_bits & (1 << i))
10796 	{
10797 	  stored_player[i].gravity =
10798 	    (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10799 	     action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10800 	     action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10801 	     stored_player[i].gravity);
10802 	}
10803       }
10804 
10805       break;
10806     }
10807 #endif
10808 
10809     case CA_SET_PLAYER_ARTWORK:
10810     {
10811       for (i = 0; i < MAX_PLAYERS; i++)
10812       {
10813 	if (trigger_player_bits & (1 << i))
10814 	{
10815 	  int artwork_element = action_arg_element;
10816 
10817 	  if (action_arg == CA_ARG_ELEMENT_RESET)
10818 	    artwork_element =
10819 	      (level.use_artwork_element[i] ? level.artwork_element[i] :
10820 	       stored_player[i].element_nr);
10821 
10822 #if USE_GFX_RESET_PLAYER_ARTWORK
10823 	  if (stored_player[i].artwork_element != artwork_element)
10824 	    stored_player[i].Frame = 0;
10825 #endif
10826 
10827 	  stored_player[i].artwork_element = artwork_element;
10828 
10829 	  SetPlayerWaiting(&stored_player[i], FALSE);
10830 
10831 	  /* set number of special actions for bored and sleeping animation */
10832 	  stored_player[i].num_special_action_bored =
10833 	    get_num_special_action(artwork_element,
10834 				   ACTION_BORING_1, ACTION_BORING_LAST);
10835 	  stored_player[i].num_special_action_sleeping =
10836 	    get_num_special_action(artwork_element,
10837 				   ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10838 	}
10839       }
10840 
10841       break;
10842     }
10843 
10844     case CA_SET_PLAYER_INVENTORY:
10845     {
10846       for (i = 0; i < MAX_PLAYERS; i++)
10847       {
10848 	struct PlayerInfo *player = &stored_player[i];
10849 	int j, k;
10850 
10851 	if (trigger_player_bits & (1 << i))
10852 	{
10853 	  int inventory_element = action_arg_element;
10854 
10855 	  if (action_arg == CA_ARG_ELEMENT_TARGET ||
10856 	      action_arg == CA_ARG_ELEMENT_TRIGGER ||
10857 	      action_arg == CA_ARG_ELEMENT_ACTION)
10858 	  {
10859 	    int element = inventory_element;
10860 	    int collect_count = element_info[element].collect_count_initial;
10861 
10862 	    if (!IS_CUSTOM_ELEMENT(element))
10863 	      collect_count = 1;
10864 
10865 	    if (collect_count == 0)
10866 	      player->inventory_infinite_element = element;
10867 	    else
10868 	      for (k = 0; k < collect_count; k++)
10869 		if (player->inventory_size < MAX_INVENTORY_SIZE)
10870 		  player->inventory_element[player->inventory_size++] =
10871 		    element;
10872 	  }
10873 	  else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10874 		   action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10875 		   action_arg == CA_ARG_INVENTORY_RM_ACTION)
10876 	  {
10877 	    if (player->inventory_infinite_element != EL_UNDEFINED &&
10878 		IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10879 				     action_arg_element_raw))
10880 	      player->inventory_infinite_element = EL_UNDEFINED;
10881 
10882 	    for (k = 0, j = 0; j < player->inventory_size; j++)
10883 	    {
10884 	      if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10885 					action_arg_element_raw))
10886 		player->inventory_element[k++] = player->inventory_element[j];
10887 	    }
10888 
10889 	    player->inventory_size = k;
10890 	  }
10891 	  else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10892 	  {
10893 	    if (player->inventory_size > 0)
10894 	    {
10895 	      for (j = 0; j < player->inventory_size - 1; j++)
10896 		player->inventory_element[j] = player->inventory_element[j + 1];
10897 
10898 	      player->inventory_size--;
10899 	    }
10900 	  }
10901 	  else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10902 	  {
10903 	    if (player->inventory_size > 0)
10904 	      player->inventory_size--;
10905 	  }
10906 	  else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10907 	  {
10908 	    player->inventory_infinite_element = EL_UNDEFINED;
10909 	    player->inventory_size = 0;
10910 	  }
10911 	  else if (action_arg == CA_ARG_INVENTORY_RESET)
10912 	  {
10913 	    player->inventory_infinite_element = EL_UNDEFINED;
10914 	    player->inventory_size = 0;
10915 
10916 	    if (level.use_initial_inventory[i])
10917 	    {
10918 	      for (j = 0; j < level.initial_inventory_size[i]; j++)
10919 	      {
10920 		int element = level.initial_inventory_content[i][j];
10921 		int collect_count = element_info[element].collect_count_initial;
10922 
10923 		if (!IS_CUSTOM_ELEMENT(element))
10924 		  collect_count = 1;
10925 
10926 		if (collect_count == 0)
10927 		  player->inventory_infinite_element = element;
10928 		else
10929 		  for (k = 0; k < collect_count; k++)
10930 		    if (player->inventory_size < MAX_INVENTORY_SIZE)
10931 		      player->inventory_element[player->inventory_size++] =
10932 			element;
10933 	      }
10934 	    }
10935 	  }
10936 	}
10937       }
10938 
10939       break;
10940     }
10941 
10942     /* ---------- CE actions  ---------------------------------------------- */
10943 
10944     case CA_SET_CE_VALUE:
10945     {
10946 #if USE_NEW_CUSTOM_VALUE
10947       int last_ce_value = CustomValue[x][y];
10948 
10949       CustomValue[x][y] = action_arg_number_new;
10950 
10951       if (CustomValue[x][y] != last_ce_value)
10952       {
10953 	CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10954 	CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10955 
10956 	if (CustomValue[x][y] == 0)
10957 	{
10958 	  CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10959 	  CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10960 	}
10961       }
10962 #endif
10963 
10964       break;
10965     }
10966 
10967     case CA_SET_CE_SCORE:
10968     {
10969 #if USE_NEW_CUSTOM_VALUE
10970       int last_ce_score = ei->collect_score;
10971 
10972       ei->collect_score = action_arg_number_new;
10973 
10974       if (ei->collect_score != last_ce_score)
10975       {
10976 	CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10977 	CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10978 
10979 	if (ei->collect_score == 0)
10980 	{
10981 	  int xx, yy;
10982 
10983 	  CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10984 	  CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10985 
10986 	  /*
10987 	    This is a very special case that seems to be a mixture between
10988 	    CheckElementChange() and CheckTriggeredElementChange(): while
10989 	    the first one only affects single elements that are triggered
10990 	    directly, the second one affects multiple elements in the playfield
10991 	    that are triggered indirectly by another element. This is a third
10992 	    case: Changing the CE score always affects multiple identical CEs,
10993 	    so every affected CE must be checked, not only the single CE for
10994 	    which the CE score was changed in the first place (as every instance
10995 	    of that CE shares the same CE score, and therefore also can change)!
10996 	  */
10997 	  SCAN_PLAYFIELD(xx, yy)
10998 	  {
10999 	    if (Feld[xx][yy] == element)
11000 	      CheckElementChange(xx, yy, element, EL_UNDEFINED,
11001 				 CE_SCORE_GETS_ZERO);
11002 	  }
11003 	}
11004       }
11005 #endif
11006 
11007       break;
11008     }
11009 
11010     case CA_SET_CE_ARTWORK:
11011     {
11012       int artwork_element = action_arg_element;
11013       boolean reset_frame = FALSE;
11014       int xx, yy;
11015 
11016       if (action_arg == CA_ARG_ELEMENT_RESET)
11017 	artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
11018 			   element);
11019 
11020       if (ei->gfx_element != artwork_element)
11021 	reset_frame = TRUE;
11022 
11023       ei->gfx_element = artwork_element;
11024 
11025       SCAN_PLAYFIELD(xx, yy)
11026       {
11027 	if (Feld[xx][yy] == element)
11028 	{
11029 	  if (reset_frame)
11030 	  {
11031 	    ResetGfxAnimation(xx, yy);
11032 	    ResetRandomAnimationValue(xx, yy);
11033 	  }
11034 
11035 	  TEST_DrawLevelField(xx, yy);
11036 	}
11037       }
11038 
11039       break;
11040     }
11041 
11042     /* ---------- engine actions  ------------------------------------------ */
11043 
11044     case CA_SET_ENGINE_SCAN_MODE:
11045     {
11046       InitPlayfieldScanMode(action_arg);
11047 
11048       break;
11049     }
11050 
11051     default:
11052       break;
11053   }
11054 }
11055 
CreateFieldExt(int x,int y,int element,boolean is_change)11056 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11057 {
11058   int old_element = Feld[x][y];
11059   int new_element = GetElementFromGroupElement(element);
11060   int previous_move_direction = MovDir[x][y];
11061 #if USE_NEW_CUSTOM_VALUE
11062   int last_ce_value = CustomValue[x][y];
11063 #endif
11064   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11065   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11066   boolean add_player_onto_element = (new_element_is_player &&
11067 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11068 				     /* this breaks SnakeBite when a snake is
11069 					halfway through a door that closes */
11070 				     /* NOW FIXED AT LEVEL INIT IN files.c */
11071 				     new_element != EL_SOKOBAN_FIELD_PLAYER &&
11072 #endif
11073 				     IS_WALKABLE(old_element));
11074 
11075 #if 0
11076   /* check if element under the player changes from accessible to unaccessible
11077      (needed for special case of dropping element which then changes) */
11078   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11079       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11080   {
11081     Bang(x, y);
11082 
11083     return;
11084   }
11085 #endif
11086 
11087   if (!add_player_onto_element)
11088   {
11089     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11090       RemoveMovingField(x, y);
11091     else
11092       RemoveField(x, y);
11093 
11094     Feld[x][y] = new_element;
11095 
11096 #if !USE_GFX_RESET_GFX_ANIMATION
11097     ResetGfxAnimation(x, y);
11098     ResetRandomAnimationValue(x, y);
11099 #endif
11100 
11101     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11102       MovDir[x][y] = previous_move_direction;
11103 
11104 #if USE_NEW_CUSTOM_VALUE
11105     if (element_info[new_element].use_last_ce_value)
11106       CustomValue[x][y] = last_ce_value;
11107 #endif
11108 
11109     InitField_WithBug1(x, y, FALSE);
11110 
11111     new_element = Feld[x][y];	/* element may have changed */
11112 
11113 #if USE_GFX_RESET_GFX_ANIMATION
11114     ResetGfxAnimation(x, y);
11115     ResetRandomAnimationValue(x, y);
11116 #endif
11117 
11118     TEST_DrawLevelField(x, y);
11119 
11120     if (GFX_CRUMBLED(new_element))
11121       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
11122   }
11123 
11124 #if 1
11125   /* check if element under the player changes from accessible to unaccessible
11126      (needed for special case of dropping element which then changes) */
11127   /* (must be checked after creating new element for walkable group elements) */
11128 #if USE_FIX_KILLED_BY_NON_WALKABLE
11129   if (IS_PLAYER(x, y) && !player_explosion_protected &&
11130       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11131   {
11132     Bang(x, y);
11133 
11134     return;
11135   }
11136 #else
11137   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11138       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11139   {
11140     Bang(x, y);
11141 
11142     return;
11143   }
11144 #endif
11145 #endif
11146 
11147   /* "ChangeCount" not set yet to allow "entered by player" change one time */
11148   if (new_element_is_player)
11149     RelocatePlayer(x, y, new_element);
11150 
11151   if (is_change)
11152     ChangeCount[x][y]++;	/* count number of changes in the same frame */
11153 
11154   TestIfBadThingTouchesPlayer(x, y);
11155   TestIfPlayerTouchesCustomElement(x, y);
11156   TestIfElementTouchesCustomElement(x, y);
11157 }
11158 
CreateField(int x,int y,int element)11159 static void CreateField(int x, int y, int element)
11160 {
11161   CreateFieldExt(x, y, element, FALSE);
11162 }
11163 
CreateElementFromChange(int x,int y,int element)11164 static void CreateElementFromChange(int x, int y, int element)
11165 {
11166   element = GET_VALID_RUNTIME_ELEMENT(element);
11167 
11168 #if USE_STOP_CHANGED_ELEMENTS
11169   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11170   {
11171     int old_element = Feld[x][y];
11172 
11173     /* prevent changed element from moving in same engine frame
11174        unless both old and new element can either fall or move */
11175     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11176 	(!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11177       Stop[x][y] = TRUE;
11178   }
11179 #endif
11180 
11181   CreateFieldExt(x, y, element, TRUE);
11182 }
11183 
ChangeElement(int x,int y,int element,int page)11184 static boolean ChangeElement(int x, int y, int element, int page)
11185 {
11186   struct ElementInfo *ei = &element_info[element];
11187   struct ElementChangeInfo *change = &ei->change_page[page];
11188   int ce_value = CustomValue[x][y];
11189   int ce_score = ei->collect_score;
11190   int target_element;
11191   int old_element = Feld[x][y];
11192 
11193   /* always use default change event to prevent running into a loop */
11194   if (ChangeEvent[x][y] == -1)
11195     ChangeEvent[x][y] = CE_DELAY;
11196 
11197   if (ChangeEvent[x][y] == CE_DELAY)
11198   {
11199     /* reset actual trigger element, trigger player and action element */
11200     change->actual_trigger_element = EL_EMPTY;
11201     change->actual_trigger_player = EL_EMPTY;
11202     change->actual_trigger_player_bits = CH_PLAYER_NONE;
11203     change->actual_trigger_side = CH_SIDE_NONE;
11204     change->actual_trigger_ce_value = 0;
11205     change->actual_trigger_ce_score = 0;
11206   }
11207 
11208   /* do not change elements more than a specified maximum number of changes */
11209   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11210     return FALSE;
11211 
11212   ChangeCount[x][y]++;		/* count number of changes in the same frame */
11213 
11214   if (change->explode)
11215   {
11216     Bang(x, y);
11217 
11218     return TRUE;
11219   }
11220 
11221   if (change->use_target_content)
11222   {
11223     boolean complete_replace = TRUE;
11224     boolean can_replace[3][3];
11225     int xx, yy;
11226 
11227     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11228     {
11229       boolean is_empty;
11230       boolean is_walkable;
11231       boolean is_diggable;
11232       boolean is_collectible;
11233       boolean is_removable;
11234       boolean is_destructible;
11235       int ex = x + xx - 1;
11236       int ey = y + yy - 1;
11237       int content_element = change->target_content.e[xx][yy];
11238       int e;
11239 
11240       can_replace[xx][yy] = TRUE;
11241 
11242       if (ex == x && ey == y)	/* do not check changing element itself */
11243 	continue;
11244 
11245       if (content_element == EL_EMPTY_SPACE)
11246       {
11247 	can_replace[xx][yy] = FALSE;	/* do not replace border with space */
11248 
11249 	continue;
11250       }
11251 
11252       if (!IN_LEV_FIELD(ex, ey))
11253       {
11254 	can_replace[xx][yy] = FALSE;
11255 	complete_replace = FALSE;
11256 
11257 	continue;
11258       }
11259 
11260       e = Feld[ex][ey];
11261 
11262       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11263 	e = MovingOrBlocked2Element(ex, ey);
11264 
11265       is_empty = (IS_FREE(ex, ey) ||
11266 		  (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11267 
11268       is_walkable     = (is_empty || IS_WALKABLE(e));
11269       is_diggable     = (is_empty || IS_DIGGABLE(e));
11270       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
11271       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11272       is_removable    = (is_diggable || is_collectible);
11273 
11274       can_replace[xx][yy] =
11275 	(((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
11276 	  (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
11277 	  (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
11278 	  (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
11279 	  (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
11280 	  (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11281 	 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11282 
11283       if (!can_replace[xx][yy])
11284 	complete_replace = FALSE;
11285     }
11286 
11287     if (!change->only_if_complete || complete_replace)
11288     {
11289       boolean something_has_changed = FALSE;
11290 
11291       if (change->only_if_complete && change->use_random_replace &&
11292 	  RND(100) < change->random_percentage)
11293 	return FALSE;
11294 
11295       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11296       {
11297 	int ex = x + xx - 1;
11298 	int ey = y + yy - 1;
11299 	int content_element;
11300 
11301 	if (can_replace[xx][yy] && (!change->use_random_replace ||
11302 				    RND(100) < change->random_percentage))
11303 	{
11304 	  if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11305 	    RemoveMovingField(ex, ey);
11306 
11307 	  ChangeEvent[ex][ey] = ChangeEvent[x][y];
11308 
11309 	  content_element = change->target_content.e[xx][yy];
11310 	  target_element = GET_TARGET_ELEMENT(element, content_element, change,
11311 					      ce_value, ce_score);
11312 
11313 	  CreateElementFromChange(ex, ey, target_element);
11314 
11315 	  something_has_changed = TRUE;
11316 
11317 	  /* for symmetry reasons, freeze newly created border elements */
11318 	  if (ex != x || ey != y)
11319 	    Stop[ex][ey] = TRUE;	/* no more moving in this frame */
11320 	}
11321       }
11322 
11323       if (something_has_changed)
11324       {
11325 	PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11326 	PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11327       }
11328     }
11329   }
11330   else
11331   {
11332     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11333 					ce_value, ce_score);
11334 
11335     if (element == EL_DIAGONAL_GROWING ||
11336 	element == EL_DIAGONAL_SHRINKING)
11337     {
11338       target_element = Store[x][y];
11339 
11340       Store[x][y] = EL_EMPTY;
11341     }
11342 
11343     CreateElementFromChange(x, y, target_element);
11344 
11345     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11346     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11347   }
11348 
11349   /* this uses direct change before indirect change */
11350   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11351 
11352   return TRUE;
11353 }
11354 
11355 #if USE_NEW_DELAYED_ACTION
11356 
HandleElementChange(int x,int y,int page)11357 static void HandleElementChange(int x, int y, int page)
11358 {
11359   int element = MovingOrBlocked2Element(x, y);
11360   struct ElementInfo *ei = &element_info[element];
11361   struct ElementChangeInfo *change = &ei->change_page[page];
11362   boolean handle_action_before_change = FALSE;
11363 
11364 #ifdef DEBUG
11365   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11366       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11367   {
11368     printf("\n\n");
11369     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11370 	   x, y, element, element_info[element].token_name);
11371     printf("HandleElementChange(): This should never happen!\n");
11372     printf("\n\n");
11373   }
11374 #endif
11375 
11376   /* this can happen with classic bombs on walkable, changing elements */
11377   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11378   {
11379 #if 0
11380     if (!CAN_CHANGE(Back[x][y]))	/* prevent permanent repetition */
11381       ChangeDelay[x][y] = 0;
11382 #endif
11383 
11384     return;
11385   }
11386 
11387   if (ChangeDelay[x][y] == 0)		/* initialize element change */
11388   {
11389     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11390 
11391     if (change->can_change)
11392     {
11393 #if 1
11394       /* !!! not clear why graphic animation should be reset at all here !!! */
11395       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11396 #if USE_GFX_RESET_WHEN_NOT_MOVING
11397       /* when a custom element is about to change (for example by change delay),
11398 	 do not reset graphic animation when the custom element is moving */
11399       if (!IS_MOVING(x, y))
11400 #endif
11401       {
11402 	ResetGfxAnimation(x, y);
11403 	ResetRandomAnimationValue(x, y);
11404       }
11405 #endif
11406 
11407       if (change->pre_change_function)
11408 	change->pre_change_function(x, y);
11409     }
11410   }
11411 
11412   ChangeDelay[x][y]--;
11413 
11414   if (ChangeDelay[x][y] != 0)		/* continue element change */
11415   {
11416     if (change->can_change)
11417     {
11418       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11419 
11420       if (IS_ANIMATED(graphic))
11421 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11422 
11423       if (change->change_function)
11424 	change->change_function(x, y);
11425     }
11426   }
11427   else					/* finish element change */
11428   {
11429     if (ChangePage[x][y] != -1)		/* remember page from delayed change */
11430     {
11431       page = ChangePage[x][y];
11432       ChangePage[x][y] = -1;
11433 
11434       change = &ei->change_page[page];
11435     }
11436 
11437     if (IS_MOVING(x, y))		/* never change a running system ;-) */
11438     {
11439       ChangeDelay[x][y] = 1;		/* try change after next move step */
11440       ChangePage[x][y] = page;		/* remember page to use for change */
11441 
11442       return;
11443     }
11444 
11445 #if 1
11446     /* special case: set new level random seed before changing element */
11447     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11448       handle_action_before_change = TRUE;
11449 
11450     if (change->has_action && handle_action_before_change)
11451       ExecuteCustomElementAction(x, y, element, page);
11452 #endif
11453 
11454     if (change->can_change)
11455     {
11456       if (ChangeElement(x, y, element, page))
11457       {
11458 	if (change->post_change_function)
11459 	  change->post_change_function(x, y);
11460       }
11461     }
11462 
11463     if (change->has_action && !handle_action_before_change)
11464       ExecuteCustomElementAction(x, y, element, page);
11465   }
11466 }
11467 
11468 #else
11469 
HandleElementChange(int x,int y,int page)11470 static void HandleElementChange(int x, int y, int page)
11471 {
11472   int element = MovingOrBlocked2Element(x, y);
11473   struct ElementInfo *ei = &element_info[element];
11474   struct ElementChangeInfo *change = &ei->change_page[page];
11475 
11476 #ifdef DEBUG
11477   if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11478   {
11479     printf("\n\n");
11480     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11481 	   x, y, element, element_info[element].token_name);
11482     printf("HandleElementChange(): This should never happen!\n");
11483     printf("\n\n");
11484   }
11485 #endif
11486 
11487   /* this can happen with classic bombs on walkable, changing elements */
11488   if (!CAN_CHANGE(element))
11489   {
11490 #if 0
11491     if (!CAN_CHANGE(Back[x][y]))	/* prevent permanent repetition */
11492       ChangeDelay[x][y] = 0;
11493 #endif
11494 
11495     return;
11496   }
11497 
11498   if (ChangeDelay[x][y] == 0)		/* initialize element change */
11499   {
11500     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11501 
11502     ResetGfxAnimation(x, y);
11503     ResetRandomAnimationValue(x, y);
11504 
11505     if (change->pre_change_function)
11506       change->pre_change_function(x, y);
11507   }
11508 
11509   ChangeDelay[x][y]--;
11510 
11511   if (ChangeDelay[x][y] != 0)		/* continue element change */
11512   {
11513     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11514 
11515     if (IS_ANIMATED(graphic))
11516       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11517 
11518     if (change->change_function)
11519       change->change_function(x, y);
11520   }
11521   else					/* finish element change */
11522   {
11523     if (ChangePage[x][y] != -1)		/* remember page from delayed change */
11524     {
11525       page = ChangePage[x][y];
11526       ChangePage[x][y] = -1;
11527 
11528       change = &ei->change_page[page];
11529     }
11530 
11531     if (IS_MOVING(x, y))		/* never change a running system ;-) */
11532     {
11533       ChangeDelay[x][y] = 1;		/* try change after next move step */
11534       ChangePage[x][y] = page;		/* remember page to use for change */
11535 
11536       return;
11537     }
11538 
11539     if (ChangeElement(x, y, element, page))
11540     {
11541       if (change->post_change_function)
11542 	change->post_change_function(x, y);
11543     }
11544   }
11545 }
11546 
11547 #endif
11548 
CheckTriggeredElementChangeExt(int trigger_x,int trigger_y,int trigger_element,int trigger_event,int trigger_player,int trigger_side,int trigger_page)11549 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11550 					      int trigger_element,
11551 					      int trigger_event,
11552 					      int trigger_player,
11553 					      int trigger_side,
11554 					      int trigger_page)
11555 {
11556   boolean change_done_any = FALSE;
11557   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11558   int i;
11559 
11560   if (!(trigger_events[trigger_element][trigger_event]))
11561     return FALSE;
11562 
11563 #if 0
11564   printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11565 	 trigger_event, recursion_loop_depth, recursion_loop_detected,
11566 	 recursion_loop_element, EL_NAME(recursion_loop_element));
11567 #endif
11568 
11569   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11570 
11571   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11572   {
11573     int element = EL_CUSTOM_START + i;
11574     boolean change_done = FALSE;
11575     int p;
11576 
11577     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11578 	!HAS_ANY_CHANGE_EVENT(element, trigger_event))
11579       continue;
11580 
11581     for (p = 0; p < element_info[element].num_change_pages; p++)
11582     {
11583       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11584 
11585       if (change->can_change_or_has_action &&
11586 	  change->has_event[trigger_event] &&
11587 	  change->trigger_side & trigger_side &&
11588 	  change->trigger_player & trigger_player &&
11589 	  change->trigger_page & trigger_page_bits &&
11590 	  IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11591       {
11592 	change->actual_trigger_element = trigger_element;
11593 	change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11594 	change->actual_trigger_player_bits = trigger_player;
11595 	change->actual_trigger_side = trigger_side;
11596 	change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11597 	change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11598 
11599 #if 0
11600 	printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11601 	       element, EL_NAME(element), p);
11602 #endif
11603 
11604 	if ((change->can_change && !change_done) || change->has_action)
11605 	{
11606 	  int x, y;
11607 
11608 	  SCAN_PLAYFIELD(x, y)
11609 	  {
11610 	    if (Feld[x][y] == element)
11611 	    {
11612 	      if (change->can_change && !change_done)
11613 	      {
11614 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11615 		/* if element already changed in this frame, not only prevent
11616 		   another element change (checked in ChangeElement()), but
11617 		   also prevent additional element actions for this element */
11618 
11619 		if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11620 		    !level.use_action_after_change_bug)
11621 		  continue;
11622 #endif
11623 
11624 #if 0
11625 		printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11626 		       element, EL_NAME(element), p);
11627 #endif
11628 
11629 		ChangeDelay[x][y] = 1;
11630 		ChangeEvent[x][y] = trigger_event;
11631 
11632 		HandleElementChange(x, y, p);
11633 	      }
11634 #if USE_NEW_DELAYED_ACTION
11635 	      else if (change->has_action)
11636 	      {
11637 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11638 		/* if element already changed in this frame, not only prevent
11639 		   another element change (checked in ChangeElement()), but
11640 		   also prevent additional element actions for this element */
11641 
11642 		if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11643 		    !level.use_action_after_change_bug)
11644 		  continue;
11645 #endif
11646 
11647 
11648 #if 0
11649 		printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11650 		       element, EL_NAME(element), p);
11651 #endif
11652 
11653 		ExecuteCustomElementAction(x, y, element, p);
11654 		PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11655 	      }
11656 #else
11657 	      if (change->has_action)
11658 	      {
11659 		ExecuteCustomElementAction(x, y, element, p);
11660 		PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11661 	      }
11662 #endif
11663 	    }
11664 	  }
11665 
11666 	  if (change->can_change)
11667 	  {
11668 	    change_done = TRUE;
11669 	    change_done_any = TRUE;
11670 
11671 #if 0
11672 	    printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11673 		   element, EL_NAME(element), p);
11674 #endif
11675 
11676 	  }
11677 	}
11678       }
11679     }
11680   }
11681 
11682   RECURSION_LOOP_DETECTION_END();
11683 
11684   return change_done_any;
11685 }
11686 
CheckElementChangeExt(int x,int y,int element,int trigger_element,int trigger_event,int trigger_player,int trigger_side)11687 static boolean CheckElementChangeExt(int x, int y,
11688 				     int element,
11689 				     int trigger_element,
11690 				     int trigger_event,
11691 				     int trigger_player,
11692 				     int trigger_side)
11693 {
11694   boolean change_done = FALSE;
11695   int p;
11696 
11697   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11698       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11699     return FALSE;
11700 
11701   if (Feld[x][y] == EL_BLOCKED)
11702   {
11703     Blocked2Moving(x, y, &x, &y);
11704     element = Feld[x][y];
11705   }
11706 
11707 #if 0
11708   /* check if element has already changed */
11709   if (Feld[x][y] != element)
11710     return FALSE;
11711 #else
11712   /* check if element has already changed or is about to change after moving */
11713   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11714        Feld[x][y] != element) ||
11715 
11716       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11717        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11718 	ChangePage[x][y] != -1)))
11719     return FALSE;
11720 #endif
11721 
11722 #if 0
11723   printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11724 	 trigger_event, recursion_loop_depth, recursion_loop_detected,
11725 	 recursion_loop_element, EL_NAME(recursion_loop_element));
11726 #endif
11727 
11728   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11729 
11730 #if 0
11731   printf("::: X: trigger_player_bits == %d\n", trigger_player);
11732 #endif
11733 
11734   for (p = 0; p < element_info[element].num_change_pages; p++)
11735   {
11736     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11737 
11738     /* check trigger element for all events where the element that is checked
11739        for changing interacts with a directly adjacent element -- this is
11740        different to element changes that affect other elements to change on the
11741        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11742     boolean check_trigger_element =
11743       (trigger_event == CE_TOUCHING_X ||
11744        trigger_event == CE_HITTING_X ||
11745        trigger_event == CE_HIT_BY_X ||
11746 #if 1
11747        /* this one was forgotten until 3.2.3 */
11748        trigger_event == CE_DIGGING_X);
11749 #endif
11750 
11751     if (change->can_change_or_has_action &&
11752 	change->has_event[trigger_event] &&
11753 	change->trigger_side & trigger_side &&
11754 	change->trigger_player & trigger_player &&
11755 	(!check_trigger_element ||
11756 	 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11757     {
11758       change->actual_trigger_element = trigger_element;
11759       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11760       change->actual_trigger_player_bits = trigger_player;
11761       change->actual_trigger_side = trigger_side;
11762       change->actual_trigger_ce_value = CustomValue[x][y];
11763       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11764 
11765       /* special case: trigger element not at (x,y) position for some events */
11766       if (check_trigger_element)
11767       {
11768 	static struct
11769 	{
11770 	  int dx, dy;
11771 	} move_xy[] =
11772 	  {
11773 	    {  0,  0 },
11774 	    { -1,  0 },
11775 	    { +1,  0 },
11776 	    {  0,  0 },
11777 	    {  0, -1 },
11778 	    {  0,  0 }, { 0, 0 }, { 0, 0 },
11779 	    {  0, +1 }
11780 	  };
11781 
11782 	int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11783 	int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11784 
11785 	change->actual_trigger_ce_value = CustomValue[xx][yy];
11786 	change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11787       }
11788 
11789       if (change->can_change && !change_done)
11790       {
11791 	ChangeDelay[x][y] = 1;
11792 	ChangeEvent[x][y] = trigger_event;
11793 
11794 	HandleElementChange(x, y, p);
11795 
11796 	change_done = TRUE;
11797       }
11798 #if USE_NEW_DELAYED_ACTION
11799       else if (change->has_action)
11800       {
11801 	ExecuteCustomElementAction(x, y, element, p);
11802 	PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11803       }
11804 #else
11805       if (change->has_action)
11806       {
11807 	ExecuteCustomElementAction(x, y, element, p);
11808 	PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11809       }
11810 #endif
11811     }
11812   }
11813 
11814   RECURSION_LOOP_DETECTION_END();
11815 
11816   return change_done;
11817 }
11818 
PlayPlayerSound(struct PlayerInfo * player)11819 static void PlayPlayerSound(struct PlayerInfo *player)
11820 {
11821   int jx = player->jx, jy = player->jy;
11822   int sound_element = player->artwork_element;
11823   int last_action = player->last_action_waiting;
11824   int action = player->action_waiting;
11825 
11826   if (player->is_waiting)
11827   {
11828     if (action != last_action)
11829       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11830     else
11831       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11832   }
11833   else
11834   {
11835     if (action != last_action)
11836       StopSound(element_info[sound_element].sound[last_action]);
11837 
11838     if (last_action == ACTION_SLEEPING)
11839       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11840   }
11841 }
11842 
PlayAllPlayersSound()11843 static void PlayAllPlayersSound()
11844 {
11845   int i;
11846 
11847   for (i = 0; i < MAX_PLAYERS; i++)
11848     if (stored_player[i].active)
11849       PlayPlayerSound(&stored_player[i]);
11850 }
11851 
SetPlayerWaiting(struct PlayerInfo * player,boolean is_waiting)11852 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11853 {
11854   boolean last_waiting = player->is_waiting;
11855   int move_dir = player->MovDir;
11856 
11857   player->dir_waiting = move_dir;
11858   player->last_action_waiting = player->action_waiting;
11859 
11860   if (is_waiting)
11861   {
11862     if (!last_waiting)		/* not waiting -> waiting */
11863     {
11864       player->is_waiting = TRUE;
11865 
11866       player->frame_counter_bored =
11867 	FrameCounter +
11868 	game.player_boring_delay_fixed +
11869 	GetSimpleRandom(game.player_boring_delay_random);
11870       player->frame_counter_sleeping =
11871 	FrameCounter +
11872 	game.player_sleeping_delay_fixed +
11873 	GetSimpleRandom(game.player_sleeping_delay_random);
11874 
11875       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11876     }
11877 
11878     if (game.player_sleeping_delay_fixed +
11879 	game.player_sleeping_delay_random > 0 &&
11880 	player->anim_delay_counter == 0 &&
11881 	player->post_delay_counter == 0 &&
11882 	FrameCounter >= player->frame_counter_sleeping)
11883       player->is_sleeping = TRUE;
11884     else if (game.player_boring_delay_fixed +
11885 	     game.player_boring_delay_random > 0 &&
11886 	     FrameCounter >= player->frame_counter_bored)
11887       player->is_bored = TRUE;
11888 
11889     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11890 			      player->is_bored ? ACTION_BORING :
11891 			      ACTION_WAITING);
11892 
11893     if (player->is_sleeping && player->use_murphy)
11894     {
11895       /* special case for sleeping Murphy when leaning against non-free tile */
11896 
11897       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11898 	  (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11899 	   !IS_MOVING(player->jx - 1, player->jy)))
11900 	move_dir = MV_LEFT;
11901       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11902 	       (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11903 		!IS_MOVING(player->jx + 1, player->jy)))
11904 	move_dir = MV_RIGHT;
11905       else
11906 	player->is_sleeping = FALSE;
11907 
11908       player->dir_waiting = move_dir;
11909     }
11910 
11911     if (player->is_sleeping)
11912     {
11913       if (player->num_special_action_sleeping > 0)
11914       {
11915 	if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11916 	{
11917 	  int last_special_action = player->special_action_sleeping;
11918 	  int num_special_action = player->num_special_action_sleeping;
11919 	  int special_action =
11920 	    (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11921 	     last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11922 	     last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11923 	     last_special_action + 1 : ACTION_SLEEPING);
11924 	  int special_graphic =
11925 	    el_act_dir2img(player->artwork_element, special_action, move_dir);
11926 
11927 	  player->anim_delay_counter =
11928 	    graphic_info[special_graphic].anim_delay_fixed +
11929 	    GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11930 	  player->post_delay_counter =
11931 	    graphic_info[special_graphic].post_delay_fixed +
11932 	    GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11933 
11934 	  player->special_action_sleeping = special_action;
11935 	}
11936 
11937 	if (player->anim_delay_counter > 0)
11938 	{
11939 	  player->action_waiting = player->special_action_sleeping;
11940 	  player->anim_delay_counter--;
11941 	}
11942 	else if (player->post_delay_counter > 0)
11943 	{
11944 	  player->post_delay_counter--;
11945 	}
11946       }
11947     }
11948     else if (player->is_bored)
11949     {
11950       if (player->num_special_action_bored > 0)
11951       {
11952 	if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11953 	{
11954 	  int special_action =
11955 	    ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11956 	  int special_graphic =
11957 	    el_act_dir2img(player->artwork_element, special_action, move_dir);
11958 
11959 	  player->anim_delay_counter =
11960 	    graphic_info[special_graphic].anim_delay_fixed +
11961 	    GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11962 	  player->post_delay_counter =
11963 	    graphic_info[special_graphic].post_delay_fixed +
11964 	    GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11965 
11966 	  player->special_action_bored = special_action;
11967 	}
11968 
11969 	if (player->anim_delay_counter > 0)
11970 	{
11971 	  player->action_waiting = player->special_action_bored;
11972 	  player->anim_delay_counter--;
11973 	}
11974 	else if (player->post_delay_counter > 0)
11975 	{
11976 	  player->post_delay_counter--;
11977 	}
11978       }
11979     }
11980   }
11981   else if (last_waiting)	/* waiting -> not waiting */
11982   {
11983     player->is_waiting = FALSE;
11984     player->is_bored = FALSE;
11985     player->is_sleeping = FALSE;
11986 
11987     player->frame_counter_bored = -1;
11988     player->frame_counter_sleeping = -1;
11989 
11990     player->anim_delay_counter = 0;
11991     player->post_delay_counter = 0;
11992 
11993     player->dir_waiting = player->MovDir;
11994     player->action_waiting = ACTION_DEFAULT;
11995 
11996     player->special_action_bored = ACTION_DEFAULT;
11997     player->special_action_sleeping = ACTION_DEFAULT;
11998   }
11999 }
12000 
PlayerActions(struct PlayerInfo * player,byte player_action)12001 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
12002 {
12003   boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
12004   int left	= player_action & JOY_LEFT;
12005   int right	= player_action & JOY_RIGHT;
12006   int up	= player_action & JOY_UP;
12007   int down	= player_action & JOY_DOWN;
12008   int button1	= player_action & JOY_BUTTON_1;
12009   int button2	= player_action & JOY_BUTTON_2;
12010   int dx	= (left ? -1 : right ? 1 : 0);
12011   int dy	= (up   ? -1 : down  ? 1 : 0);
12012 
12013   if (!player->active || tape.pausing)
12014     return 0;
12015 
12016   if (player_action)
12017   {
12018     if (button1)
12019       snapped = SnapField(player, dx, dy);
12020     else
12021     {
12022       if (button2)
12023 	dropped = DropElement(player);
12024 
12025       moved = MovePlayer(player, dx, dy);
12026     }
12027 
12028     if (tape.single_step && tape.recording && !tape.pausing)
12029     {
12030 #if 1
12031       /* as it is called "single step mode", just return to pause mode when the
12032 	 player stopped moving after one tile (or never starts moving at all) */
12033       if (!player->is_moving)
12034 #else
12035       /* this is buggy: there are quite some cases where the single step mode
12036 	 does not return to pause mode (like pushing things that don't move
12037 	 or simply by trying to run against a wall) */
12038       if (button1 || (dropped && !moved))
12039 #endif
12040       {
12041 	TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12042 	SnapField(player, 0, 0);		/* stop snapping */
12043       }
12044     }
12045 
12046     SetPlayerWaiting(player, FALSE);
12047 
12048     return player_action;
12049   }
12050   else
12051   {
12052     /* no actions for this player (no input at player's configured device) */
12053 
12054     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
12055     SnapField(player, 0, 0);
12056     CheckGravityMovementWhenNotMoving(player);
12057 
12058     if (player->MovPos == 0)
12059       SetPlayerWaiting(player, TRUE);
12060 
12061     if (player->MovPos == 0)	/* needed for tape.playing */
12062       player->is_moving = FALSE;
12063 
12064     player->is_dropping = FALSE;
12065     player->is_dropping_pressed = FALSE;
12066     player->drop_pressed_delay = 0;
12067 
12068     return 0;
12069   }
12070 }
12071 
CheckLevelTime()12072 static void CheckLevelTime()
12073 {
12074   int i;
12075 
12076   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
12077   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12078   {
12079     if (level.native_em_level->lev->home == 0)	/* all players at home */
12080     {
12081       PlayerWins(local_player);
12082 
12083       AllPlayersGone = TRUE;
12084 
12085       level.native_em_level->lev->home = -1;
12086     }
12087 
12088     if (level.native_em_level->ply[0]->alive == 0 &&
12089 	level.native_em_level->ply[1]->alive == 0 &&
12090 	level.native_em_level->ply[2]->alive == 0 &&
12091 	level.native_em_level->ply[3]->alive == 0)	/* all dead */
12092       AllPlayersGone = TRUE;
12093   }
12094   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12095   {
12096     if (game_sp.LevelSolved &&
12097 	!game_sp.GameOver)				/* game won */
12098     {
12099       PlayerWins(local_player);
12100 
12101       game_sp.GameOver = TRUE;
12102 
12103       AllPlayersGone = TRUE;
12104     }
12105 
12106     if (game_sp.GameOver)				/* game lost */
12107       AllPlayersGone = TRUE;
12108   }
12109 
12110   if (TimeFrames >= FRAMES_PER_SECOND)
12111   {
12112     TimeFrames = 0;
12113     TapeTime++;
12114 
12115     for (i = 0; i < MAX_PLAYERS; i++)
12116     {
12117       struct PlayerInfo *player = &stored_player[i];
12118 
12119       if (SHIELD_ON(player))
12120       {
12121 	player->shield_normal_time_left--;
12122 
12123 	if (player->shield_deadly_time_left > 0)
12124 	  player->shield_deadly_time_left--;
12125       }
12126     }
12127 
12128     if (!local_player->LevelSolved && !level.use_step_counter)
12129     {
12130       TimePlayed++;
12131 
12132       if (TimeLeft > 0)
12133       {
12134 	TimeLeft--;
12135 
12136 	if (TimeLeft <= 10 && setup.time_limit)
12137 	  PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12138 
12139 #if 1
12140 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12141 
12142 	DisplayGameControlValues();
12143 #else
12144 	DrawGameValue_Time(TimeLeft);
12145 #endif
12146 
12147 	if (!TimeLeft && setup.time_limit)
12148 	{
12149 	  if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12150 	    level.native_em_level->lev->killed_out_of_time = TRUE;
12151 	  else
12152 	    for (i = 0; i < MAX_PLAYERS; i++)
12153 	      KillPlayer(&stored_player[i]);
12154 	}
12155       }
12156 #if 1
12157       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12158       {
12159 	game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12160 
12161 	DisplayGameControlValues();
12162       }
12163 #else
12164       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12165 	DrawGameValue_Time(TimePlayed);
12166 #endif
12167 
12168       level.native_em_level->lev->time =
12169 	(level.time == 0 ? TimePlayed : TimeLeft);
12170     }
12171 
12172     if (tape.recording || tape.playing)
12173       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12174   }
12175 
12176 #if 1
12177   UpdateAndDisplayGameControlValues();
12178 #else
12179   UpdateGameDoorValues();
12180   DrawGameDoorValues();
12181 #endif
12182 }
12183 
AdvanceFrameAndPlayerCounters(int player_nr)12184 void AdvanceFrameAndPlayerCounters(int player_nr)
12185 {
12186   int i;
12187 
12188   /* advance frame counters (global frame counter and time frame counter) */
12189   FrameCounter++;
12190   TimeFrames++;
12191 
12192   /* advance player counters (counters for move delay, move animation etc.) */
12193   for (i = 0; i < MAX_PLAYERS; i++)
12194   {
12195     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12196     int move_delay_value = stored_player[i].move_delay_value;
12197     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12198 
12199     if (!advance_player_counters)	/* not all players may be affected */
12200       continue;
12201 
12202 #if USE_NEW_PLAYER_ANIM
12203     if (move_frames == 0)	/* less than one move per game frame */
12204     {
12205       int stepsize = TILEX / move_delay_value;
12206       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12207       int count = (stored_player[i].is_moving ?
12208 		   ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12209 
12210       if (count % delay == 0)
12211 	move_frames = 1;
12212     }
12213 #endif
12214 
12215     stored_player[i].Frame += move_frames;
12216 
12217     if (stored_player[i].MovPos != 0)
12218       stored_player[i].StepFrame += move_frames;
12219 
12220     if (stored_player[i].move_delay > 0)
12221       stored_player[i].move_delay--;
12222 
12223     /* due to bugs in previous versions, counter must count up, not down */
12224     if (stored_player[i].push_delay != -1)
12225       stored_player[i].push_delay++;
12226 
12227     if (stored_player[i].drop_delay > 0)
12228       stored_player[i].drop_delay--;
12229 
12230     if (stored_player[i].is_dropping_pressed)
12231       stored_player[i].drop_pressed_delay++;
12232   }
12233 }
12234 
StartGameActions(boolean init_network_game,boolean record_tape,long random_seed)12235 void StartGameActions(boolean init_network_game, boolean record_tape,
12236 		      long random_seed)
12237 {
12238   unsigned long new_random_seed = InitRND(random_seed);
12239 
12240   if (record_tape)
12241     TapeStartRecording(new_random_seed);
12242 
12243 #if defined(NETWORK_AVALIABLE)
12244   if (init_network_game)
12245   {
12246     SendToServer_StartPlaying();
12247 
12248     return;
12249   }
12250 #endif
12251 
12252   InitGame();
12253 }
12254 
GameActions()12255 void GameActions()
12256 {
12257   static unsigned long game_frame_delay = 0;
12258   unsigned long game_frame_delay_value;
12259   byte *recorded_player_action;
12260   byte summarized_player_action = 0;
12261   byte tape_action[MAX_PLAYERS];
12262   int i;
12263 
12264   /* detect endless loops, caused by custom element programming */
12265   if (recursion_loop_detected && recursion_loop_depth == 0)
12266   {
12267     char *message = getStringCat3("Internal Error ! Element ",
12268 				  EL_NAME(recursion_loop_element),
12269 				  " caused endless loop ! Quit the game ?");
12270 
12271     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12272 	  EL_NAME(recursion_loop_element));
12273 
12274     RequestQuitGameExt(FALSE, level_editor_test_game, message);
12275 
12276     recursion_loop_detected = FALSE;	/* if game should be continued */
12277 
12278     free(message);
12279 
12280     return;
12281   }
12282 
12283   if (game.restart_level)
12284     StartGameActions(options.network, setup.autorecord, level.random_seed);
12285 
12286   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
12287   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12288   {
12289     if (level.native_em_level->lev->home == 0)	/* all players at home */
12290     {
12291       PlayerWins(local_player);
12292 
12293       AllPlayersGone = TRUE;
12294 
12295       level.native_em_level->lev->home = -1;
12296     }
12297 
12298     if (level.native_em_level->ply[0]->alive == 0 &&
12299 	level.native_em_level->ply[1]->alive == 0 &&
12300 	level.native_em_level->ply[2]->alive == 0 &&
12301 	level.native_em_level->ply[3]->alive == 0)	/* all dead */
12302       AllPlayersGone = TRUE;
12303   }
12304   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12305   {
12306     if (game_sp.LevelSolved &&
12307 	!game_sp.GameOver)				/* game won */
12308     {
12309       PlayerWins(local_player);
12310 
12311       game_sp.GameOver = TRUE;
12312 
12313       AllPlayersGone = TRUE;
12314     }
12315 
12316     if (game_sp.GameOver)				/* game lost */
12317       AllPlayersGone = TRUE;
12318   }
12319 
12320   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12321     GameWon();
12322 
12323   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12324     TapeStop();
12325 
12326   if (game_status != GAME_MODE_PLAYING)		/* status might have changed */
12327     return;
12328 
12329   game_frame_delay_value =
12330     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12331 
12332   if (tape.playing && tape.warp_forward && !tape.pausing)
12333     game_frame_delay_value = 0;
12334 
12335   /* ---------- main game synchronization point ---------- */
12336 
12337   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12338 
12339   if (network_playing && !network_player_action_received)
12340   {
12341     /* try to get network player actions in time */
12342 
12343 #if defined(NETWORK_AVALIABLE)
12344     /* last chance to get network player actions without main loop delay */
12345     HandleNetworking();
12346 #endif
12347 
12348     /* game was quit by network peer */
12349     if (game_status != GAME_MODE_PLAYING)
12350       return;
12351 
12352     if (!network_player_action_received)
12353       return;		/* failed to get network player actions in time */
12354 
12355     /* do not yet reset "network_player_action_received" (for tape.pausing) */
12356   }
12357 
12358   if (tape.pausing)
12359     return;
12360 
12361   /* at this point we know that we really continue executing the game */
12362 
12363   network_player_action_received = FALSE;
12364 
12365   /* when playing tape, read previously recorded player input from tape data */
12366   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12367 
12368 #if 1
12369   /* TapePlayAction() may return NULL when toggling to "pause before death" */
12370   if (tape.pausing)
12371     return;
12372 #endif
12373 
12374   if (tape.set_centered_player)
12375   {
12376     game.centered_player_nr_next = tape.centered_player_nr_next;
12377     game.set_centered_player = TRUE;
12378   }
12379 
12380   for (i = 0; i < MAX_PLAYERS; i++)
12381   {
12382     summarized_player_action |= stored_player[i].action;
12383 
12384     if (!network_playing)
12385       stored_player[i].effective_action = stored_player[i].action;
12386   }
12387 
12388 #if defined(NETWORK_AVALIABLE)
12389   if (network_playing)
12390     SendToServer_MovePlayer(summarized_player_action);
12391 #endif
12392 
12393   if (!options.network && !setup.team_mode)
12394     local_player->effective_action = summarized_player_action;
12395 
12396   if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12397   {
12398     for (i = 0; i < MAX_PLAYERS; i++)
12399       stored_player[i].effective_action =
12400 	(i == game.centered_player_nr ? summarized_player_action : 0);
12401   }
12402 
12403   if (recorded_player_action != NULL)
12404     for (i = 0; i < MAX_PLAYERS; i++)
12405       stored_player[i].effective_action = recorded_player_action[i];
12406 
12407   for (i = 0; i < MAX_PLAYERS; i++)
12408   {
12409     tape_action[i] = stored_player[i].effective_action;
12410 
12411     /* (this can only happen in the R'n'D game engine) */
12412     if (tape.recording && tape_action[i] && !tape.player_participates[i])
12413       tape.player_participates[i] = TRUE;    /* player just appeared from CE */
12414   }
12415 
12416   /* only record actions from input devices, but not programmed actions */
12417   if (tape.recording)
12418     TapeRecordAction(tape_action);
12419 
12420 #if USE_NEW_PLAYER_ASSIGNMENTS
12421   {
12422     byte mapped_action[MAX_PLAYERS];
12423 
12424     for (i = 0; i < MAX_PLAYERS; i++)
12425       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12426 
12427     for (i = 0; i < MAX_PLAYERS; i++)
12428       stored_player[i].effective_action = mapped_action[i];
12429   }
12430 #endif
12431 
12432   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12433   {
12434     GameActions_EM_Main();
12435   }
12436   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12437   {
12438     GameActions_SP_Main();
12439   }
12440   else
12441   {
12442     GameActions_RND();
12443   }
12444 }
12445 
GameActions_EM_Main()12446 void GameActions_EM_Main()
12447 {
12448   byte effective_action[MAX_PLAYERS];
12449   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12450   int i;
12451 
12452   for (i = 0; i < MAX_PLAYERS; i++)
12453     effective_action[i] = stored_player[i].effective_action;
12454 
12455   GameActions_EM(effective_action, warp_mode);
12456 
12457   CheckLevelTime();
12458 
12459   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
12460 }
12461 
GameActions_SP_Main()12462 void GameActions_SP_Main()
12463 {
12464   byte effective_action[MAX_PLAYERS];
12465   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12466   int i;
12467 
12468   for (i = 0; i < MAX_PLAYERS; i++)
12469     effective_action[i] = stored_player[i].effective_action;
12470 
12471   GameActions_SP(effective_action, warp_mode);
12472 
12473   CheckLevelTime();
12474 
12475   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
12476 }
12477 
GameActions_RND()12478 void GameActions_RND()
12479 {
12480   int magic_wall_x = 0, magic_wall_y = 0;
12481   int i, x, y, element, graphic;
12482 
12483   InitPlayfieldScanModeVars();
12484 
12485 #if USE_ONE_MORE_CHANGE_PER_FRAME
12486   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12487   {
12488     SCAN_PLAYFIELD(x, y)
12489     {
12490       ChangeCount[x][y] = 0;
12491       ChangeEvent[x][y] = -1;
12492     }
12493   }
12494 #endif
12495 
12496   if (game.set_centered_player)
12497   {
12498     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12499 
12500     /* switching to "all players" only possible if all players fit to screen */
12501     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12502     {
12503       game.centered_player_nr_next = game.centered_player_nr;
12504       game.set_centered_player = FALSE;
12505     }
12506 
12507     /* do not switch focus to non-existing (or non-active) player */
12508     if (game.centered_player_nr_next >= 0 &&
12509 	!stored_player[game.centered_player_nr_next].active)
12510     {
12511       game.centered_player_nr_next = game.centered_player_nr;
12512       game.set_centered_player = FALSE;
12513     }
12514   }
12515 
12516   if (game.set_centered_player &&
12517       ScreenMovPos == 0)	/* screen currently aligned at tile position */
12518   {
12519     int sx, sy;
12520 
12521     if (game.centered_player_nr_next == -1)
12522     {
12523       setScreenCenteredToAllPlayers(&sx, &sy);
12524     }
12525     else
12526     {
12527       sx = stored_player[game.centered_player_nr_next].jx;
12528       sy = stored_player[game.centered_player_nr_next].jy;
12529     }
12530 
12531     game.centered_player_nr = game.centered_player_nr_next;
12532     game.set_centered_player = FALSE;
12533 
12534     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12535     DrawGameDoorValues();
12536   }
12537 
12538   for (i = 0; i < MAX_PLAYERS; i++)
12539   {
12540     int actual_player_action = stored_player[i].effective_action;
12541 
12542 #if 1
12543     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12544        - rnd_equinox_tetrachloride 048
12545        - rnd_equinox_tetrachloride_ii 096
12546        - rnd_emanuel_schmieg 002
12547        - doctor_sloan_ww 001, 020
12548     */
12549     if (stored_player[i].MovPos == 0)
12550       CheckGravityMovement(&stored_player[i]);
12551 #endif
12552 
12553     /* overwrite programmed action with tape action */
12554     if (stored_player[i].programmed_action)
12555       actual_player_action = stored_player[i].programmed_action;
12556 
12557     PlayerActions(&stored_player[i], actual_player_action);
12558 
12559     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12560   }
12561 
12562   ScrollScreen(NULL, SCROLL_GO_ON);
12563 
12564   /* for backwards compatibility, the following code emulates a fixed bug that
12565      occured when pushing elements (causing elements that just made their last
12566      pushing step to already (if possible) make their first falling step in the
12567      same game frame, which is bad); this code is also needed to use the famous
12568      "spring push bug" which is used in older levels and might be wanted to be
12569      used also in newer levels, but in this case the buggy pushing code is only
12570      affecting the "spring" element and no other elements */
12571 
12572   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12573   {
12574     for (i = 0; i < MAX_PLAYERS; i++)
12575     {
12576       struct PlayerInfo *player = &stored_player[i];
12577       int x = player->jx;
12578       int y = player->jy;
12579 
12580       if (player->active && player->is_pushing && player->is_moving &&
12581 	  IS_MOVING(x, y) &&
12582 	  (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12583 	   Feld[x][y] == EL_SPRING))
12584       {
12585 	ContinueMoving(x, y);
12586 
12587 	/* continue moving after pushing (this is actually a bug) */
12588 	if (!IS_MOVING(x, y))
12589 	  Stop[x][y] = FALSE;
12590       }
12591     }
12592   }
12593 
12594 #if 0
12595   debug_print_timestamp(0, "start main loop profiling");
12596 #endif
12597 
12598   SCAN_PLAYFIELD(x, y)
12599   {
12600     ChangeCount[x][y] = 0;
12601     ChangeEvent[x][y] = -1;
12602 
12603     /* this must be handled before main playfield loop */
12604     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12605     {
12606       MovDelay[x][y]--;
12607       if (MovDelay[x][y] <= 0)
12608 	RemoveField(x, y);
12609     }
12610 
12611 #if USE_NEW_SNAP_DELAY
12612     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12613     {
12614       MovDelay[x][y]--;
12615       if (MovDelay[x][y] <= 0)
12616       {
12617 	RemoveField(x, y);
12618 	TEST_DrawLevelField(x, y);
12619 
12620 	TestIfElementTouchesCustomElement(x, y);	/* for empty space */
12621       }
12622     }
12623 #endif
12624 
12625 #if DEBUG
12626     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12627     {
12628       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12629       printf("GameActions(): This should never happen!\n");
12630 
12631       ChangePage[x][y] = -1;
12632     }
12633 #endif
12634 
12635     Stop[x][y] = FALSE;
12636     if (WasJustMoving[x][y] > 0)
12637       WasJustMoving[x][y]--;
12638     if (WasJustFalling[x][y] > 0)
12639       WasJustFalling[x][y]--;
12640     if (CheckCollision[x][y] > 0)
12641       CheckCollision[x][y]--;
12642     if (CheckImpact[x][y] > 0)
12643       CheckImpact[x][y]--;
12644 
12645     GfxFrame[x][y]++;
12646 
12647     /* reset finished pushing action (not done in ContinueMoving() to allow
12648        continuous pushing animation for elements with zero push delay) */
12649     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12650     {
12651       ResetGfxAnimation(x, y);
12652       TEST_DrawLevelField(x, y);
12653     }
12654 
12655 #if DEBUG
12656     if (IS_BLOCKED(x, y))
12657     {
12658       int oldx, oldy;
12659 
12660       Blocked2Moving(x, y, &oldx, &oldy);
12661       if (!IS_MOVING(oldx, oldy))
12662       {
12663 	printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12664 	printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12665 	printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12666 	printf("GameActions(): This should never happen!\n");
12667       }
12668     }
12669 #endif
12670   }
12671 
12672 #if 0
12673   debug_print_timestamp(0, "- time for pre-main loop:");
12674 #endif
12675 
12676 #if 0	// -------------------- !!! TEST ONLY !!! --------------------
12677   SCAN_PLAYFIELD(x, y)
12678   {
12679     element = Feld[x][y];
12680     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12681 
12682 #if 1
12683     {
12684 #if 1
12685       int element2 = element;
12686       int graphic2 = graphic;
12687 #else
12688       int element2 = Feld[x][y];
12689       int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12690 #endif
12691       int last_gfx_frame = GfxFrame[x][y];
12692 
12693       if (graphic_info[graphic2].anim_global_sync)
12694 	GfxFrame[x][y] = FrameCounter;
12695       else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12696 	GfxFrame[x][y] = CustomValue[x][y];
12697       else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12698 	GfxFrame[x][y] = element_info[element2].collect_score;
12699       else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12700 	GfxFrame[x][y] = ChangeDelay[x][y];
12701 
12702       if (redraw && GfxFrame[x][y] != last_gfx_frame)
12703 	DrawLevelGraphicAnimation(x, y, graphic2);
12704     }
12705 #else
12706     ResetGfxFrame(x, y, TRUE);
12707 #endif
12708 
12709 #if 1
12710     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12711 	IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12712       ResetRandomAnimationValue(x, y);
12713 #endif
12714 
12715 #if 1
12716     SetRandomAnimationValue(x, y);
12717 #endif
12718 
12719 #if 1
12720     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12721 #endif
12722   }
12723 #endif	// -------------------- !!! TEST ONLY !!! --------------------
12724 
12725 #if 0
12726   debug_print_timestamp(0, "- time for TEST loop:     -->");
12727 #endif
12728 
12729   SCAN_PLAYFIELD(x, y)
12730   {
12731     element = Feld[x][y];
12732     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12733 
12734     ResetGfxFrame(x, y, TRUE);
12735 
12736     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12737 	IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12738       ResetRandomAnimationValue(x, y);
12739 
12740     SetRandomAnimationValue(x, y);
12741 
12742     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12743 
12744     if (IS_INACTIVE(element))
12745     {
12746       if (IS_ANIMATED(graphic))
12747 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12748 
12749       continue;
12750     }
12751 
12752     /* this may take place after moving, so 'element' may have changed */
12753     if (IS_CHANGING(x, y) &&
12754 	(game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12755     {
12756       int page = element_info[element].event_page_nr[CE_DELAY];
12757 
12758 #if 1
12759       HandleElementChange(x, y, page);
12760 #else
12761       if (CAN_CHANGE(element))
12762 	HandleElementChange(x, y, page);
12763 
12764       if (HAS_ACTION(element))
12765 	ExecuteCustomElementAction(x, y, element, page);
12766 #endif
12767 
12768       element = Feld[x][y];
12769       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12770     }
12771 
12772 #if 0	// ---------------------------------------------------------------------
12773 
12774     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12775     {
12776       StartMoving(x, y);
12777 
12778       element = Feld[x][y];
12779       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12780 
12781       if (IS_ANIMATED(graphic) &&
12782 	  !IS_MOVING(x, y) &&
12783 	  !Stop[x][y])
12784 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12785 
12786       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12787 	TEST_DrawTwinkleOnField(x, y);
12788     }
12789     else if (IS_MOVING(x, y))
12790       ContinueMoving(x, y);
12791     else
12792     {
12793       switch (element)
12794       {
12795         case EL_ACID:
12796         case EL_EXIT_OPEN:
12797         case EL_EM_EXIT_OPEN:
12798         case EL_SP_EXIT_OPEN:
12799         case EL_STEEL_EXIT_OPEN:
12800         case EL_EM_STEEL_EXIT_OPEN:
12801         case EL_SP_TERMINAL:
12802         case EL_SP_TERMINAL_ACTIVE:
12803         case EL_EXTRA_TIME:
12804         case EL_SHIELD_NORMAL:
12805         case EL_SHIELD_DEADLY:
12806 	  if (IS_ANIMATED(graphic))
12807 	    DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12808 	  break;
12809 
12810         case EL_DYNAMITE_ACTIVE:
12811         case EL_EM_DYNAMITE_ACTIVE:
12812         case EL_DYNABOMB_PLAYER_1_ACTIVE:
12813         case EL_DYNABOMB_PLAYER_2_ACTIVE:
12814         case EL_DYNABOMB_PLAYER_3_ACTIVE:
12815         case EL_DYNABOMB_PLAYER_4_ACTIVE:
12816         case EL_SP_DISK_RED_ACTIVE:
12817 	  CheckDynamite(x, y);
12818 	  break;
12819 
12820         case EL_AMOEBA_GROWING:
12821 	  AmoebeWaechst(x, y);
12822 	  break;
12823 
12824         case EL_AMOEBA_SHRINKING:
12825 	  AmoebaDisappearing(x, y);
12826 	  break;
12827 
12828 #if !USE_NEW_AMOEBA_CODE
12829         case EL_AMOEBA_WET:
12830         case EL_AMOEBA_DRY:
12831         case EL_AMOEBA_FULL:
12832         case EL_BD_AMOEBA:
12833         case EL_EMC_DRIPPER:
12834 	  AmoebeAbleger(x, y);
12835 	  break;
12836 #endif
12837 
12838         case EL_GAME_OF_LIFE:
12839         case EL_BIOMAZE:
12840 	  Life(x, y);
12841 	  break;
12842 
12843         case EL_EXIT_CLOSED:
12844 	  CheckExit(x, y);
12845 	  break;
12846 
12847         case EL_EM_EXIT_CLOSED:
12848 	  CheckExitEM(x, y);
12849 	  break;
12850 
12851         case EL_STEEL_EXIT_CLOSED:
12852 	  CheckExitSteel(x, y);
12853 	  break;
12854 
12855         case EL_EM_STEEL_EXIT_CLOSED:
12856 	  CheckExitSteelEM(x, y);
12857 	  break;
12858 
12859         case EL_SP_EXIT_CLOSED:
12860 	  CheckExitSP(x, y);
12861 	  break;
12862 
12863         case EL_EXPANDABLE_WALL_GROWING:
12864         case EL_EXPANDABLE_STEELWALL_GROWING:
12865 	  MauerWaechst(x, y);
12866 	  break;
12867 
12868         case EL_EXPANDABLE_WALL:
12869         case EL_EXPANDABLE_WALL_HORIZONTAL:
12870         case EL_EXPANDABLE_WALL_VERTICAL:
12871         case EL_EXPANDABLE_WALL_ANY:
12872         case EL_BD_EXPANDABLE_WALL:
12873 	  MauerAbleger(x, y);
12874 	  break;
12875 
12876         case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12877         case EL_EXPANDABLE_STEELWALL_VERTICAL:
12878         case EL_EXPANDABLE_STEELWALL_ANY:
12879 	  MauerAblegerStahl(x, y);
12880 	  break;
12881 
12882         case EL_FLAMES:
12883 	  CheckForDragon(x, y);
12884 	  break;
12885 
12886         case EL_EXPLOSION:
12887 	  break;
12888 
12889         case EL_ELEMENT_SNAPPING:
12890         case EL_DIAGONAL_SHRINKING:
12891         case EL_DIAGONAL_GROWING:
12892 	{
12893 	  graphic =
12894 	    el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12895 
12896 	  DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12897 	  break;
12898 	}
12899 
12900         default:
12901 	  if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12902 	    DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12903 	  break;
12904       }
12905     }
12906 
12907 #else	// ---------------------------------------------------------------------
12908 
12909     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12910     {
12911       StartMoving(x, y);
12912 
12913       element = Feld[x][y];
12914       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12915 
12916       if (IS_ANIMATED(graphic) &&
12917 	  !IS_MOVING(x, y) &&
12918 	  !Stop[x][y])
12919 	DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12920 
12921       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12922 	TEST_DrawTwinkleOnField(x, y);
12923     }
12924     else if ((element == EL_ACID ||
12925 	      element == EL_EXIT_OPEN ||
12926 	      element == EL_EM_EXIT_OPEN ||
12927 	      element == EL_SP_EXIT_OPEN ||
12928 	      element == EL_STEEL_EXIT_OPEN ||
12929 	      element == EL_EM_STEEL_EXIT_OPEN ||
12930 	      element == EL_SP_TERMINAL ||
12931 	      element == EL_SP_TERMINAL_ACTIVE ||
12932 	      element == EL_EXTRA_TIME ||
12933 	      element == EL_SHIELD_NORMAL ||
12934 	      element == EL_SHIELD_DEADLY) &&
12935 	     IS_ANIMATED(graphic))
12936       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12937     else if (IS_MOVING(x, y))
12938       ContinueMoving(x, y);
12939     else if (IS_ACTIVE_BOMB(element))
12940       CheckDynamite(x, y);
12941     else if (element == EL_AMOEBA_GROWING)
12942       AmoebeWaechst(x, y);
12943     else if (element == EL_AMOEBA_SHRINKING)
12944       AmoebaDisappearing(x, y);
12945 
12946 #if !USE_NEW_AMOEBA_CODE
12947     else if (IS_AMOEBALIVE(element))
12948       AmoebeAbleger(x, y);
12949 #endif
12950 
12951     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12952       Life(x, y);
12953     else if (element == EL_EXIT_CLOSED)
12954       CheckExit(x, y);
12955     else if (element == EL_EM_EXIT_CLOSED)
12956       CheckExitEM(x, y);
12957     else if (element == EL_STEEL_EXIT_CLOSED)
12958       CheckExitSteel(x, y);
12959     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12960       CheckExitSteelEM(x, y);
12961     else if (element == EL_SP_EXIT_CLOSED)
12962       CheckExitSP(x, y);
12963     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12964 	     element == EL_EXPANDABLE_STEELWALL_GROWING)
12965       MauerWaechst(x, y);
12966     else if (element == EL_EXPANDABLE_WALL ||
12967 	     element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12968 	     element == EL_EXPANDABLE_WALL_VERTICAL ||
12969 	     element == EL_EXPANDABLE_WALL_ANY ||
12970 	     element == EL_BD_EXPANDABLE_WALL)
12971       MauerAbleger(x, y);
12972     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12973 	     element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12974 	     element == EL_EXPANDABLE_STEELWALL_ANY)
12975       MauerAblegerStahl(x, y);
12976     else if (element == EL_FLAMES)
12977       CheckForDragon(x, y);
12978     else if (element == EL_EXPLOSION)
12979       ;	/* drawing of correct explosion animation is handled separately */
12980     else if (element == EL_ELEMENT_SNAPPING ||
12981 	     element == EL_DIAGONAL_SHRINKING ||
12982 	     element == EL_DIAGONAL_GROWING)
12983     {
12984       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12985 
12986       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12987     }
12988     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12989       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12990 
12991 #endif	// ---------------------------------------------------------------------
12992 
12993     if (IS_BELT_ACTIVE(element))
12994       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12995 
12996     if (game.magic_wall_active)
12997     {
12998       int jx = local_player->jx, jy = local_player->jy;
12999 
13000       /* play the element sound at the position nearest to the player */
13001       if ((element == EL_MAGIC_WALL_FULL ||
13002 	   element == EL_MAGIC_WALL_ACTIVE ||
13003 	   element == EL_MAGIC_WALL_EMPTYING ||
13004 	   element == EL_BD_MAGIC_WALL_FULL ||
13005 	   element == EL_BD_MAGIC_WALL_ACTIVE ||
13006 	   element == EL_BD_MAGIC_WALL_EMPTYING ||
13007 	   element == EL_DC_MAGIC_WALL_FULL ||
13008 	   element == EL_DC_MAGIC_WALL_ACTIVE ||
13009 	   element == EL_DC_MAGIC_WALL_EMPTYING) &&
13010 	  ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
13011       {
13012 	magic_wall_x = x;
13013 	magic_wall_y = y;
13014       }
13015     }
13016   }
13017 
13018 #if 0
13019   debug_print_timestamp(0, "- time for MAIN loop:     -->");
13020 #endif
13021 
13022 #if USE_NEW_AMOEBA_CODE
13023   /* new experimental amoeba growth stuff */
13024   if (!(FrameCounter % 8))
13025   {
13026     static unsigned long random = 1684108901;
13027 
13028     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
13029     {
13030       x = RND(lev_fieldx);
13031       y = RND(lev_fieldy);
13032       element = Feld[x][y];
13033 
13034       if (!IS_PLAYER(x,y) &&
13035 	  (element == EL_EMPTY ||
13036 	   CAN_GROW_INTO(element) ||
13037 	   element == EL_QUICKSAND_EMPTY ||
13038 	   element == EL_QUICKSAND_FAST_EMPTY ||
13039 	   element == EL_ACID_SPLASH_LEFT ||
13040 	   element == EL_ACID_SPLASH_RIGHT))
13041       {
13042 	if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
13043 	    (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
13044 	    (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
13045 	    (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
13046 	  Feld[x][y] = EL_AMOEBA_DROP;
13047       }
13048 
13049       random = random * 129 + 1;
13050     }
13051   }
13052 #endif
13053 
13054 #if 0
13055   if (game.explosions_delayed)
13056 #endif
13057   {
13058     game.explosions_delayed = FALSE;
13059 
13060     SCAN_PLAYFIELD(x, y)
13061     {
13062       element = Feld[x][y];
13063 
13064       if (ExplodeField[x][y])
13065 	Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
13066       else if (element == EL_EXPLOSION)
13067 	Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
13068 
13069       ExplodeField[x][y] = EX_TYPE_NONE;
13070     }
13071 
13072     game.explosions_delayed = TRUE;
13073   }
13074 
13075   if (game.magic_wall_active)
13076   {
13077     if (!(game.magic_wall_time_left % 4))
13078     {
13079       int element = Feld[magic_wall_x][magic_wall_y];
13080 
13081       if (element == EL_BD_MAGIC_WALL_FULL ||
13082 	  element == EL_BD_MAGIC_WALL_ACTIVE ||
13083 	  element == EL_BD_MAGIC_WALL_EMPTYING)
13084 	PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
13085       else if (element == EL_DC_MAGIC_WALL_FULL ||
13086 	       element == EL_DC_MAGIC_WALL_ACTIVE ||
13087 	       element == EL_DC_MAGIC_WALL_EMPTYING)
13088 	PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
13089       else
13090 	PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
13091     }
13092 
13093     if (game.magic_wall_time_left > 0)
13094     {
13095       game.magic_wall_time_left--;
13096 
13097       if (!game.magic_wall_time_left)
13098       {
13099 	SCAN_PLAYFIELD(x, y)
13100 	{
13101 	  element = Feld[x][y];
13102 
13103 	  if (element == EL_MAGIC_WALL_ACTIVE ||
13104 	      element == EL_MAGIC_WALL_FULL)
13105 	  {
13106 	    Feld[x][y] = EL_MAGIC_WALL_DEAD;
13107 	    TEST_DrawLevelField(x, y);
13108 	  }
13109 	  else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
13110 		   element == EL_BD_MAGIC_WALL_FULL)
13111 	  {
13112 	    Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13113 	    TEST_DrawLevelField(x, y);
13114 	  }
13115 	  else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13116 		   element == EL_DC_MAGIC_WALL_FULL)
13117 	  {
13118 	    Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13119 	    TEST_DrawLevelField(x, y);
13120 	  }
13121 	}
13122 
13123 	game.magic_wall_active = FALSE;
13124       }
13125     }
13126   }
13127 
13128   if (game.light_time_left > 0)
13129   {
13130     game.light_time_left--;
13131 
13132     if (game.light_time_left == 0)
13133       RedrawAllLightSwitchesAndInvisibleElements();
13134   }
13135 
13136   if (game.timegate_time_left > 0)
13137   {
13138     game.timegate_time_left--;
13139 
13140     if (game.timegate_time_left == 0)
13141       CloseAllOpenTimegates();
13142   }
13143 
13144   if (game.lenses_time_left > 0)
13145   {
13146     game.lenses_time_left--;
13147 
13148     if (game.lenses_time_left == 0)
13149       RedrawAllInvisibleElementsForLenses();
13150   }
13151 
13152   if (game.magnify_time_left > 0)
13153   {
13154     game.magnify_time_left--;
13155 
13156     if (game.magnify_time_left == 0)
13157       RedrawAllInvisibleElementsForMagnifier();
13158   }
13159 
13160   for (i = 0; i < MAX_PLAYERS; i++)
13161   {
13162     struct PlayerInfo *player = &stored_player[i];
13163 
13164     if (SHIELD_ON(player))
13165     {
13166       if (player->shield_deadly_time_left)
13167 	PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13168       else if (player->shield_normal_time_left)
13169 	PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13170     }
13171   }
13172 
13173 #if USE_DELAYED_GFX_REDRAW
13174   SCAN_PLAYFIELD(x, y)
13175   {
13176 #if 1
13177     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13178 #else
13179     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13180 	GfxRedraw[x][y] != GFX_REDRAW_NONE)
13181 #endif
13182     {
13183       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13184 	 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13185 
13186       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13187 	DrawLevelField(x, y);
13188 
13189       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13190 	DrawLevelFieldCrumbled(x, y);
13191 
13192       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13193 	DrawLevelFieldCrumbledNeighbours(x, y);
13194 
13195       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13196 	DrawTwinkleOnField(x, y);
13197     }
13198 
13199     GfxRedraw[x][y] = GFX_REDRAW_NONE;
13200   }
13201 #endif
13202 
13203   CheckLevelTime();
13204 
13205   DrawAllPlayers();
13206   PlayAllPlayersSound();
13207 
13208   if (options.debug)			/* calculate frames per second */
13209   {
13210     static unsigned long fps_counter = 0;
13211     static int fps_frames = 0;
13212     unsigned long fps_delay_ms = Counter() - fps_counter;
13213 
13214     fps_frames++;
13215 
13216     if (fps_delay_ms >= 500)	/* calculate fps every 0.5 seconds */
13217     {
13218       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13219 
13220       fps_frames = 0;
13221       fps_counter = Counter();
13222     }
13223 
13224     redraw_mask |= REDRAW_FPS;
13225   }
13226 
13227   AdvanceFrameAndPlayerCounters(-1);	/* advance counters for all players */
13228 
13229   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13230   {
13231     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13232 
13233     local_player->show_envelope = 0;
13234   }
13235 
13236 #if 0
13237   debug_print_timestamp(0, "stop main loop profiling ");
13238   printf("----------------------------------------------------------\n");
13239 #endif
13240 
13241   /* use random number generator in every frame to make it less predictable */
13242   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13243     RND(1);
13244 }
13245 
AllPlayersInSight(struct PlayerInfo * player,int x,int y)13246 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13247 {
13248   int min_x = x, min_y = y, max_x = x, max_y = y;
13249   int i;
13250 
13251   for (i = 0; i < MAX_PLAYERS; i++)
13252   {
13253     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13254 
13255     if (!stored_player[i].active || &stored_player[i] == player)
13256       continue;
13257 
13258     min_x = MIN(min_x, jx);
13259     min_y = MIN(min_y, jy);
13260     max_x = MAX(max_x, jx);
13261     max_y = MAX(max_y, jy);
13262   }
13263 
13264   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13265 }
13266 
AllPlayersInVisibleScreen()13267 static boolean AllPlayersInVisibleScreen()
13268 {
13269   int i;
13270 
13271   for (i = 0; i < MAX_PLAYERS; i++)
13272   {
13273     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13274 
13275     if (!stored_player[i].active)
13276       continue;
13277 
13278     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13279       return FALSE;
13280   }
13281 
13282   return TRUE;
13283 }
13284 
ScrollLevel(int dx,int dy)13285 void ScrollLevel(int dx, int dy)
13286 {
13287 #if 0
13288   /* (directly solved in BlitBitmap() now) */
13289   static Bitmap *bitmap_db_field2 = NULL;
13290   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13291   int x, y;
13292 #else
13293   int x, y;
13294 #endif
13295 
13296 #if 0
13297   /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13298   /* only horizontal XOR vertical scroll direction allowed */
13299   if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13300     return;
13301 #endif
13302 
13303 #if 0
13304   /* (directly solved in BlitBitmap() now) */
13305   if (bitmap_db_field2 == NULL)
13306     bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13307 
13308   /* needed when blitting directly to same bitmap -- should not be needed with
13309      recent SDL libraries, but apparently does not work in 1.2.11 directly */
13310   BlitBitmap(drawto_field, bitmap_db_field2,
13311 	     FX + TILEX * (dx == -1) - softscroll_offset,
13312 	     FY + TILEY * (dy == -1) - softscroll_offset,
13313 	     SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13314 	     SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13315 	     FX + TILEX * (dx == 1) - softscroll_offset,
13316 	     FY + TILEY * (dy == 1) - softscroll_offset);
13317   BlitBitmap(bitmap_db_field2, drawto_field,
13318 	     FX + TILEX * (dx == 1) - softscroll_offset,
13319 	     FY + TILEY * (dy == 1) - softscroll_offset,
13320 	     SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13321 	     SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13322 	     FX + TILEX * (dx == 1) - softscroll_offset,
13323 	     FY + TILEY * (dy == 1) - softscroll_offset);
13324 
13325 #else
13326 
13327 #if 0
13328   /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13329   int xsize = (BX2 - BX1 + 1);
13330   int ysize = (BY2 - BY1 + 1);
13331   int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13332   int end   = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13333   int step  = (start < end ? +1 : -1);
13334 
13335   for (i = start; i != end; i += step)
13336   {
13337     BlitBitmap(drawto_field, drawto_field,
13338 	       FX + TILEX * (dx != 0 ? i + step : 0),
13339 	       FY + TILEY * (dy != 0 ? i + step : 0),
13340 	       TILEX * (dx != 0 ? 1 : xsize),
13341 	       TILEY * (dy != 0 ? 1 : ysize),
13342 	       FX + TILEX * (dx != 0 ? i : 0),
13343 	       FY + TILEY * (dy != 0 ? i : 0));
13344   }
13345 
13346 #else
13347 
13348   int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13349 
13350   BlitBitmap(drawto_field, drawto_field,
13351 	     FX + TILEX * (dx == -1) - softscroll_offset,
13352 	     FY + TILEY * (dy == -1) - softscroll_offset,
13353 	     SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13354 	     SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13355 	     FX + TILEX * (dx == 1) - softscroll_offset,
13356 	     FY + TILEY * (dy == 1) - softscroll_offset);
13357 #endif
13358 #endif
13359 
13360   if (dx != 0)
13361   {
13362     x = (dx == 1 ? BX1 : BX2);
13363     for (y = BY1; y <= BY2; y++)
13364       DrawScreenField(x, y);
13365   }
13366 
13367   if (dy != 0)
13368   {
13369     y = (dy == 1 ? BY1 : BY2);
13370     for (x = BX1; x <= BX2; x++)
13371       DrawScreenField(x, y);
13372   }
13373 
13374   redraw_mask |= REDRAW_FIELD;
13375 }
13376 
canFallDown(struct PlayerInfo * player)13377 static boolean canFallDown(struct PlayerInfo *player)
13378 {
13379   int jx = player->jx, jy = player->jy;
13380 
13381   return (IN_LEV_FIELD(jx, jy + 1) &&
13382 	  (IS_FREE(jx, jy + 1) ||
13383 	   (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13384 	  IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13385 	  !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13386 }
13387 
canPassField(int x,int y,int move_dir)13388 static boolean canPassField(int x, int y, int move_dir)
13389 {
13390   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13391   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13392   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13393   int nextx = x + dx;
13394   int nexty = y + dy;
13395   int element = Feld[x][y];
13396 
13397   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13398 	  !CAN_MOVE(element) &&
13399 	  IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13400 	  IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13401 	  (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13402 }
13403 
canMoveToValidFieldWithGravity(int x,int y,int move_dir)13404 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13405 {
13406   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13407   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13408   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13409   int newx = x + dx;
13410   int newy = y + dy;
13411 
13412   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13413 	  IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13414 	  (IS_DIGGABLE(Feld[newx][newy]) ||
13415 	   IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13416 	   canPassField(newx, newy, move_dir)));
13417 }
13418 
CheckGravityMovement(struct PlayerInfo * player)13419 static void CheckGravityMovement(struct PlayerInfo *player)
13420 {
13421 #if USE_PLAYER_GRAVITY
13422   if (player->gravity && !player->programmed_action)
13423 #else
13424   if (game.gravity && !player->programmed_action)
13425 #endif
13426   {
13427     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13428     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13429     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13430     int jx = player->jx, jy = player->jy;
13431     boolean player_is_moving_to_valid_field =
13432       (!player_is_snapping &&
13433        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13434 	canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13435     boolean player_can_fall_down = canFallDown(player);
13436 
13437     if (player_can_fall_down &&
13438 	!player_is_moving_to_valid_field)
13439       player->programmed_action = MV_DOWN;
13440   }
13441 }
13442 
CheckGravityMovementWhenNotMoving(struct PlayerInfo * player)13443 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13444 {
13445   return CheckGravityMovement(player);
13446 
13447 #if USE_PLAYER_GRAVITY
13448   if (player->gravity && !player->programmed_action)
13449 #else
13450   if (game.gravity && !player->programmed_action)
13451 #endif
13452   {
13453     int jx = player->jx, jy = player->jy;
13454     boolean field_under_player_is_free =
13455       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13456     boolean player_is_standing_on_valid_field =
13457       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13458        (IS_WALKABLE(Feld[jx][jy]) &&
13459 	!(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13460 
13461     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13462       player->programmed_action = MV_DOWN;
13463   }
13464 }
13465 
13466 /*
13467   MovePlayerOneStep()
13468   -----------------------------------------------------------------------------
13469   dx, dy:		direction (non-diagonal) to try to move the player to
13470   real_dx, real_dy:	direction as read from input device (can be diagonal)
13471 */
13472 
MovePlayerOneStep(struct PlayerInfo * player,int dx,int dy,int real_dx,int real_dy)13473 boolean MovePlayerOneStep(struct PlayerInfo *player,
13474 			  int dx, int dy, int real_dx, int real_dy)
13475 {
13476   int jx = player->jx, jy = player->jy;
13477   int new_jx = jx + dx, new_jy = jy + dy;
13478 #if !USE_FIXED_DONT_RUN_INTO
13479   int element;
13480 #endif
13481   int can_move;
13482   boolean player_can_move = !player->cannot_move;
13483 
13484   if (!player->active || (!dx && !dy))
13485     return MP_NO_ACTION;
13486 
13487   player->MovDir = (dx < 0 ? MV_LEFT :
13488 		    dx > 0 ? MV_RIGHT :
13489 		    dy < 0 ? MV_UP :
13490 		    dy > 0 ? MV_DOWN :	MV_NONE);
13491 
13492   if (!IN_LEV_FIELD(new_jx, new_jy))
13493     return MP_NO_ACTION;
13494 
13495   if (!player_can_move)
13496   {
13497     if (player->MovPos == 0)
13498     {
13499       player->is_moving = FALSE;
13500       player->is_digging = FALSE;
13501       player->is_collecting = FALSE;
13502       player->is_snapping = FALSE;
13503       player->is_pushing = FALSE;
13504     }
13505   }
13506 
13507 #if 1
13508   if (!options.network && game.centered_player_nr == -1 &&
13509       !AllPlayersInSight(player, new_jx, new_jy))
13510     return MP_NO_ACTION;
13511 #else
13512   if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13513     return MP_NO_ACTION;
13514 #endif
13515 
13516 #if !USE_FIXED_DONT_RUN_INTO
13517   element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13518 
13519   /* (moved to DigField()) */
13520   if (player_can_move && DONT_RUN_INTO(element))
13521   {
13522     if (element == EL_ACID && dx == 0 && dy == 1)
13523     {
13524       SplashAcid(new_jx, new_jy);
13525       Feld[jx][jy] = EL_PLAYER_1;
13526       InitMovingField(jx, jy, MV_DOWN);
13527       Store[jx][jy] = EL_ACID;
13528       ContinueMoving(jx, jy);
13529       BuryPlayer(player);
13530     }
13531     else
13532       TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13533 
13534     return MP_MOVING;
13535   }
13536 #endif
13537 
13538   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13539   if (can_move != MP_MOVING)
13540     return can_move;
13541 
13542   /* check if DigField() has caused relocation of the player */
13543   if (player->jx != jx || player->jy != jy)
13544     return MP_NO_ACTION;	/* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13545 
13546   StorePlayer[jx][jy] = 0;
13547   player->last_jx = jx;
13548   player->last_jy = jy;
13549   player->jx = new_jx;
13550   player->jy = new_jy;
13551   StorePlayer[new_jx][new_jy] = player->element_nr;
13552 
13553   if (player->move_delay_value_next != -1)
13554   {
13555     player->move_delay_value = player->move_delay_value_next;
13556     player->move_delay_value_next = -1;
13557   }
13558 
13559   player->MovPos =
13560     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13561 
13562   player->step_counter++;
13563 
13564   PlayerVisit[jx][jy] = FrameCounter;
13565 
13566 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13567   player->is_moving = TRUE;
13568 #endif
13569 
13570 #if 1
13571   /* should better be called in MovePlayer(), but this breaks some tapes */
13572   ScrollPlayer(player, SCROLL_INIT);
13573 #endif
13574 
13575   return MP_MOVING;
13576 }
13577 
MovePlayer(struct PlayerInfo * player,int dx,int dy)13578 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13579 {
13580   int jx = player->jx, jy = player->jy;
13581   int old_jx = jx, old_jy = jy;
13582   int moved = MP_NO_ACTION;
13583 
13584   if (!player->active)
13585     return FALSE;
13586 
13587   if (!dx && !dy)
13588   {
13589     if (player->MovPos == 0)
13590     {
13591       player->is_moving = FALSE;
13592       player->is_digging = FALSE;
13593       player->is_collecting = FALSE;
13594       player->is_snapping = FALSE;
13595       player->is_pushing = FALSE;
13596     }
13597 
13598     return FALSE;
13599   }
13600 
13601   if (player->move_delay > 0)
13602     return FALSE;
13603 
13604   player->move_delay = -1;		/* set to "uninitialized" value */
13605 
13606   /* store if player is automatically moved to next field */
13607   player->is_auto_moving = (player->programmed_action != MV_NONE);
13608 
13609   /* remove the last programmed player action */
13610   player->programmed_action = 0;
13611 
13612   if (player->MovPos)
13613   {
13614     /* should only happen if pre-1.2 tape recordings are played */
13615     /* this is only for backward compatibility */
13616 
13617     int original_move_delay_value = player->move_delay_value;
13618 
13619 #if DEBUG
13620     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13621 	   tape.counter);
13622 #endif
13623 
13624     /* scroll remaining steps with finest movement resolution */
13625     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13626 
13627     while (player->MovPos)
13628     {
13629       ScrollPlayer(player, SCROLL_GO_ON);
13630       ScrollScreen(NULL, SCROLL_GO_ON);
13631 
13632       AdvanceFrameAndPlayerCounters(player->index_nr);
13633 
13634       DrawAllPlayers();
13635       BackToFront();
13636     }
13637 
13638     player->move_delay_value = original_move_delay_value;
13639   }
13640 
13641   player->is_active = FALSE;
13642 
13643   if (player->last_move_dir & MV_HORIZONTAL)
13644   {
13645     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13646       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13647   }
13648   else
13649   {
13650     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13651       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13652   }
13653 
13654 #if USE_FIXED_BORDER_RUNNING_GFX
13655   if (!moved && !player->is_active)
13656   {
13657     player->is_moving = FALSE;
13658     player->is_digging = FALSE;
13659     player->is_collecting = FALSE;
13660     player->is_snapping = FALSE;
13661     player->is_pushing = FALSE;
13662   }
13663 #endif
13664 
13665   jx = player->jx;
13666   jy = player->jy;
13667 
13668 #if 1
13669   if (moved & MP_MOVING && !ScreenMovPos &&
13670       (player->index_nr == game.centered_player_nr ||
13671        game.centered_player_nr == -1))
13672 #else
13673   if (moved & MP_MOVING && !ScreenMovPos &&
13674       (player == local_player || !options.network))
13675 #endif
13676   {
13677     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13678     int offset = game.scroll_delay_value;
13679 
13680     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13681     {
13682       /* actual player has left the screen -- scroll in that direction */
13683       if (jx != old_jx)		/* player has moved horizontally */
13684 	scroll_x += (jx - old_jx);
13685       else			/* player has moved vertically */
13686 	scroll_y += (jy - old_jy);
13687     }
13688     else
13689     {
13690       if (jx != old_jx)		/* player has moved horizontally */
13691       {
13692  	if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
13693 	    (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13694 	  scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13695 
13696 	/* don't scroll over playfield boundaries */
13697 	if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13698 	  scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13699 
13700 	/* don't scroll more than one field at a time */
13701 	scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13702 
13703 	/* don't scroll against the player's moving direction */
13704 	if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13705 	    (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13706 	  scroll_x = old_scroll_x;
13707       }
13708       else			/* player has moved vertically */
13709       {
13710 	if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
13711 	    (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13712 	  scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13713 
13714 	/* don't scroll over playfield boundaries */
13715 	if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13716 	  scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13717 
13718 	/* don't scroll more than one field at a time */
13719 	scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13720 
13721 	/* don't scroll against the player's moving direction */
13722 	if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13723 	    (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13724 	  scroll_y = old_scroll_y;
13725       }
13726     }
13727 
13728     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13729     {
13730 #if 1
13731       if (!options.network && game.centered_player_nr == -1 &&
13732 	  !AllPlayersInVisibleScreen())
13733       {
13734 	scroll_x = old_scroll_x;
13735 	scroll_y = old_scroll_y;
13736       }
13737       else
13738 #else
13739       if (!options.network && !AllPlayersInVisibleScreen())
13740       {
13741 	scroll_x = old_scroll_x;
13742 	scroll_y = old_scroll_y;
13743       }
13744       else
13745 #endif
13746       {
13747 	ScrollScreen(player, SCROLL_INIT);
13748 	ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13749       }
13750     }
13751   }
13752 
13753   player->StepFrame = 0;
13754 
13755   if (moved & MP_MOVING)
13756   {
13757     if (old_jx != jx && old_jy == jy)
13758       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13759     else if (old_jx == jx && old_jy != jy)
13760       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13761 
13762     TEST_DrawLevelField(jx, jy);	/* for "crumbled sand" */
13763 
13764     player->last_move_dir = player->MovDir;
13765     player->is_moving = TRUE;
13766     player->is_snapping = FALSE;
13767     player->is_switching = FALSE;
13768     player->is_dropping = FALSE;
13769     player->is_dropping_pressed = FALSE;
13770     player->drop_pressed_delay = 0;
13771 
13772 #if 0
13773     /* should better be called here than above, but this breaks some tapes */
13774     ScrollPlayer(player, SCROLL_INIT);
13775 #endif
13776   }
13777   else
13778   {
13779     CheckGravityMovementWhenNotMoving(player);
13780 
13781     player->is_moving = FALSE;
13782 
13783     /* at this point, the player is allowed to move, but cannot move right now
13784        (e.g. because of something blocking the way) -- ensure that the player
13785        is also allowed to move in the next frame (in old versions before 3.1.1,
13786        the player was forced to wait again for eight frames before next try) */
13787 
13788     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13789       player->move_delay = 0;	/* allow direct movement in the next frame */
13790   }
13791 
13792   if (player->move_delay == -1)		/* not yet initialized by DigField() */
13793     player->move_delay = player->move_delay_value;
13794 
13795   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13796   {
13797     TestIfPlayerTouchesBadThing(jx, jy);
13798     TestIfPlayerTouchesCustomElement(jx, jy);
13799   }
13800 
13801   if (!player->active)
13802     RemovePlayer(player);
13803 
13804   return moved;
13805 }
13806 
ScrollPlayer(struct PlayerInfo * player,int mode)13807 void ScrollPlayer(struct PlayerInfo *player, int mode)
13808 {
13809   int jx = player->jx, jy = player->jy;
13810   int last_jx = player->last_jx, last_jy = player->last_jy;
13811   int move_stepsize = TILEX / player->move_delay_value;
13812 
13813 #if USE_NEW_PLAYER_SPEED
13814   if (!player->active)
13815     return;
13816 
13817   if (player->MovPos == 0 && mode == SCROLL_GO_ON)	/* player not moving */
13818     return;
13819 #else
13820   if (!player->active || player->MovPos == 0)
13821     return;
13822 #endif
13823 
13824   if (mode == SCROLL_INIT)
13825   {
13826     player->actual_frame_counter = FrameCounter;
13827     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13828 
13829     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13830 	Feld[last_jx][last_jy] == EL_EMPTY)
13831     {
13832       int last_field_block_delay = 0;	/* start with no blocking at all */
13833       int block_delay_adjustment = player->block_delay_adjustment;
13834 
13835       /* if player blocks last field, add delay for exactly one move */
13836       if (player->block_last_field)
13837       {
13838 	last_field_block_delay += player->move_delay_value;
13839 
13840 	/* when blocking enabled, prevent moving up despite gravity */
13841 #if USE_PLAYER_GRAVITY
13842 	if (player->gravity && player->MovDir == MV_UP)
13843 	  block_delay_adjustment = -1;
13844 #else
13845 	if (game.gravity && player->MovDir == MV_UP)
13846 	  block_delay_adjustment = -1;
13847 #endif
13848       }
13849 
13850       /* add block delay adjustment (also possible when not blocking) */
13851       last_field_block_delay += block_delay_adjustment;
13852 
13853       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13854       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13855     }
13856 
13857 #if USE_NEW_PLAYER_SPEED
13858     if (player->MovPos != 0)	/* player has not yet reached destination */
13859       return;
13860 #else
13861     return;
13862 #endif
13863   }
13864   else if (!FrameReached(&player->actual_frame_counter, 1))
13865     return;
13866 
13867 #if USE_NEW_PLAYER_SPEED
13868   if (player->MovPos != 0)
13869   {
13870     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13871     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13872 
13873     /* before DrawPlayer() to draw correct player graphic for this case */
13874     if (player->MovPos == 0)
13875       CheckGravityMovement(player);
13876   }
13877 #else
13878   player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13879   player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13880 
13881   /* before DrawPlayer() to draw correct player graphic for this case */
13882   if (player->MovPos == 0)
13883     CheckGravityMovement(player);
13884 #endif
13885 
13886   if (player->MovPos == 0)	/* player reached destination field */
13887   {
13888     if (player->move_delay_reset_counter > 0)
13889     {
13890       player->move_delay_reset_counter--;
13891 
13892       if (player->move_delay_reset_counter == 0)
13893       {
13894 	/* continue with normal speed after quickly moving through gate */
13895 	HALVE_PLAYER_SPEED(player);
13896 
13897 	/* be able to make the next move without delay */
13898 	player->move_delay = 0;
13899       }
13900     }
13901 
13902     player->last_jx = jx;
13903     player->last_jy = jy;
13904 
13905     if (Feld[jx][jy] == EL_EXIT_OPEN ||
13906 	Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13907 #if 1
13908 	Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13909 #endif
13910 	Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13911 	Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13912 #if 1
13913 	Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13914 #endif
13915 	Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13916 	Feld[jx][jy] == EL_SP_EXIT_OPENING)	/* <-- special case */
13917     {
13918       DrawPlayer(player);	/* needed here only to cleanup last field */
13919       RemovePlayer(player);
13920 
13921       if (local_player->friends_still_needed == 0 ||
13922 	  IS_SP_ELEMENT(Feld[jx][jy]))
13923 	PlayerWins(player);
13924     }
13925 
13926     /* this breaks one level: "machine", level 000 */
13927     {
13928       int move_direction = player->MovDir;
13929       int enter_side = MV_DIR_OPPOSITE(move_direction);
13930       int leave_side = move_direction;
13931       int old_jx = last_jx;
13932       int old_jy = last_jy;
13933       int old_element = Feld[old_jx][old_jy];
13934       int new_element = Feld[jx][jy];
13935 
13936       if (IS_CUSTOM_ELEMENT(old_element))
13937 	CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13938 				   CE_LEFT_BY_PLAYER,
13939 				   player->index_bit, leave_side);
13940 
13941       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13942 					  CE_PLAYER_LEAVES_X,
13943 					  player->index_bit, leave_side);
13944 
13945       if (IS_CUSTOM_ELEMENT(new_element))
13946 	CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13947 				   player->index_bit, enter_side);
13948 
13949       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13950 					  CE_PLAYER_ENTERS_X,
13951 					  player->index_bit, enter_side);
13952 
13953 #if USE_FIX_CE_ACTION_WITH_PLAYER
13954       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13955 					CE_MOVE_OF_X, move_direction);
13956 #else
13957       CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13958 					CE_MOVE_OF_X, move_direction);
13959 #endif
13960     }
13961 
13962     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13963     {
13964       TestIfPlayerTouchesBadThing(jx, jy);
13965       TestIfPlayerTouchesCustomElement(jx, jy);
13966 
13967       /* needed because pushed element has not yet reached its destination,
13968 	 so it would trigger a change event at its previous field location */
13969       if (!player->is_pushing)
13970 	TestIfElementTouchesCustomElement(jx, jy);	/* for empty space */
13971 
13972       if (!player->active)
13973 	RemovePlayer(player);
13974     }
13975 
13976     if (!local_player->LevelSolved && level.use_step_counter)
13977     {
13978       int i;
13979 
13980       TimePlayed++;
13981 
13982       if (TimeLeft > 0)
13983       {
13984 	TimeLeft--;
13985 
13986 	if (TimeLeft <= 10 && setup.time_limit)
13987 	  PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13988 
13989 #if 1
13990 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13991 
13992 	DisplayGameControlValues();
13993 #else
13994 	DrawGameValue_Time(TimeLeft);
13995 #endif
13996 
13997 	if (!TimeLeft && setup.time_limit)
13998 	  for (i = 0; i < MAX_PLAYERS; i++)
13999 	    KillPlayer(&stored_player[i]);
14000       }
14001 #if 1
14002       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14003       {
14004 	game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
14005 
14006 	DisplayGameControlValues();
14007       }
14008 #else
14009       else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
14010 	DrawGameValue_Time(TimePlayed);
14011 #endif
14012     }
14013 
14014     if (tape.single_step && tape.recording && !tape.pausing &&
14015 	!player->programmed_action)
14016       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
14017   }
14018 }
14019 
ScrollScreen(struct PlayerInfo * player,int mode)14020 void ScrollScreen(struct PlayerInfo *player, int mode)
14021 {
14022   static unsigned long screen_frame_counter = 0;
14023 
14024   if (mode == SCROLL_INIT)
14025   {
14026     /* set scrolling step size according to actual player's moving speed */
14027     ScrollStepSize = TILEX / player->move_delay_value;
14028 
14029     screen_frame_counter = FrameCounter;
14030     ScreenMovDir = player->MovDir;
14031     ScreenMovPos = player->MovPos;
14032     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14033     return;
14034   }
14035   else if (!FrameReached(&screen_frame_counter, 1))
14036     return;
14037 
14038   if (ScreenMovPos)
14039   {
14040     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
14041     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
14042     redraw_mask |= REDRAW_FIELD;
14043   }
14044   else
14045     ScreenMovDir = MV_NONE;
14046 }
14047 
TestIfPlayerTouchesCustomElement(int x,int y)14048 void TestIfPlayerTouchesCustomElement(int x, int y)
14049 {
14050   static int xy[4][2] =
14051   {
14052     { 0, -1 },
14053     { -1, 0 },
14054     { +1, 0 },
14055     { 0, +1 }
14056   };
14057   static int trigger_sides[4][2] =
14058   {
14059     /* center side       border side */
14060     { CH_SIDE_TOP,	CH_SIDE_BOTTOM	},	/* check top    */
14061     { CH_SIDE_LEFT,	CH_SIDE_RIGHT	},	/* check left   */
14062     { CH_SIDE_RIGHT,	CH_SIDE_LEFT	},	/* check right  */
14063     { CH_SIDE_BOTTOM,	CH_SIDE_TOP	}	/* check bottom */
14064   };
14065   static int touch_dir[4] =
14066   {
14067     MV_LEFT | MV_RIGHT,
14068     MV_UP   | MV_DOWN,
14069     MV_UP   | MV_DOWN,
14070     MV_LEFT | MV_RIGHT
14071   };
14072   int center_element = Feld[x][y];	/* should always be non-moving! */
14073   int i;
14074 
14075   for (i = 0; i < NUM_DIRECTIONS; i++)
14076   {
14077     int xx = x + xy[i][0];
14078     int yy = y + xy[i][1];
14079     int center_side = trigger_sides[i][0];
14080     int border_side = trigger_sides[i][1];
14081     int border_element;
14082 
14083     if (!IN_LEV_FIELD(xx, yy))
14084       continue;
14085 
14086     if (IS_PLAYER(x, y))		/* player found at center element */
14087     {
14088       struct PlayerInfo *player = PLAYERINFO(x, y);
14089 
14090       if (game.engine_version < VERSION_IDENT(3,0,7,0))
14091 	border_element = Feld[xx][yy];		/* may be moving! */
14092       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14093 	border_element = Feld[xx][yy];
14094       else if (MovDir[xx][yy] & touch_dir[i])	/* elements are touching */
14095 	border_element = MovingOrBlocked2Element(xx, yy);
14096       else
14097 	continue;		/* center and border element do not touch */
14098 
14099       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
14100 				 player->index_bit, border_side);
14101       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
14102 					  CE_PLAYER_TOUCHES_X,
14103 					  player->index_bit, border_side);
14104 
14105 #if USE_FIX_CE_ACTION_WITH_PLAYER
14106       {
14107 	/* use player element that is initially defined in the level playfield,
14108 	   not the player element that corresponds to the runtime player number
14109 	   (example: a level that contains EL_PLAYER_3 as the only player would
14110 	   incorrectly give EL_PLAYER_1 for "player->element_nr") */
14111 	int player_element = PLAYERINFO(x, y)->initial_element;
14112 
14113 	CheckElementChangeBySide(xx, yy, border_element, player_element,
14114 				 CE_TOUCHING_X, border_side);
14115       }
14116 #endif
14117     }
14118     else if (IS_PLAYER(xx, yy))		/* player found at border element */
14119     {
14120       struct PlayerInfo *player = PLAYERINFO(xx, yy);
14121 
14122       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14123       {
14124 	if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14125 	  continue;		/* center and border element do not touch */
14126       }
14127 
14128       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14129 				 player->index_bit, center_side);
14130       CheckTriggeredElementChangeByPlayer(x, y, center_element,
14131 					  CE_PLAYER_TOUCHES_X,
14132 					  player->index_bit, center_side);
14133 
14134 #if USE_FIX_CE_ACTION_WITH_PLAYER
14135       {
14136 	/* use player element that is initially defined in the level playfield,
14137 	   not the player element that corresponds to the runtime player number
14138 	   (example: a level that contains EL_PLAYER_3 as the only player would
14139 	   incorrectly give EL_PLAYER_1 for "player->element_nr") */
14140 	int player_element = PLAYERINFO(xx, yy)->initial_element;
14141 
14142 	CheckElementChangeBySide(x, y, center_element, player_element,
14143 				 CE_TOUCHING_X, center_side);
14144       }
14145 #endif
14146 
14147       break;
14148     }
14149   }
14150 }
14151 
14152 #if USE_ELEMENT_TOUCHING_BUGFIX
14153 
TestIfElementTouchesCustomElement(int x,int y)14154 void TestIfElementTouchesCustomElement(int x, int y)
14155 {
14156   static int xy[4][2] =
14157   {
14158     { 0, -1 },
14159     { -1, 0 },
14160     { +1, 0 },
14161     { 0, +1 }
14162   };
14163   static int trigger_sides[4][2] =
14164   {
14165     /* center side	border side */
14166     { CH_SIDE_TOP,	CH_SIDE_BOTTOM	},	/* check top    */
14167     { CH_SIDE_LEFT,	CH_SIDE_RIGHT	},	/* check left   */
14168     { CH_SIDE_RIGHT,	CH_SIDE_LEFT	},	/* check right  */
14169     { CH_SIDE_BOTTOM,	CH_SIDE_TOP	}	/* check bottom */
14170   };
14171   static int touch_dir[4] =
14172   {
14173     MV_LEFT | MV_RIGHT,
14174     MV_UP   | MV_DOWN,
14175     MV_UP   | MV_DOWN,
14176     MV_LEFT | MV_RIGHT
14177   };
14178   boolean change_center_element = FALSE;
14179   int center_element = Feld[x][y];	/* should always be non-moving! */
14180   int border_element_old[NUM_DIRECTIONS];
14181   int i;
14182 
14183   for (i = 0; i < NUM_DIRECTIONS; i++)
14184   {
14185     int xx = x + xy[i][0];
14186     int yy = y + xy[i][1];
14187     int border_element;
14188 
14189     border_element_old[i] = -1;
14190 
14191     if (!IN_LEV_FIELD(xx, yy))
14192       continue;
14193 
14194     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14195       border_element = Feld[xx][yy];	/* may be moving! */
14196     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14197       border_element = Feld[xx][yy];
14198     else if (MovDir[xx][yy] & touch_dir[i])	/* elements are touching */
14199       border_element = MovingOrBlocked2Element(xx, yy);
14200     else
14201       continue;			/* center and border element do not touch */
14202 
14203     border_element_old[i] = border_element;
14204   }
14205 
14206   for (i = 0; i < NUM_DIRECTIONS; i++)
14207   {
14208     int xx = x + xy[i][0];
14209     int yy = y + xy[i][1];
14210     int center_side = trigger_sides[i][0];
14211     int border_element = border_element_old[i];
14212 
14213     if (border_element == -1)
14214       continue;
14215 
14216     /* check for change of border element */
14217     CheckElementChangeBySide(xx, yy, border_element, center_element,
14218 			     CE_TOUCHING_X, center_side);
14219 
14220     /* (center element cannot be player, so we dont have to check this here) */
14221   }
14222 
14223   for (i = 0; i < NUM_DIRECTIONS; i++)
14224   {
14225     int xx = x + xy[i][0];
14226     int yy = y + xy[i][1];
14227     int border_side = trigger_sides[i][1];
14228     int border_element = border_element_old[i];
14229 
14230     if (border_element == -1)
14231       continue;
14232 
14233     /* check for change of center element (but change it only once) */
14234     if (!change_center_element)
14235       change_center_element =
14236 	CheckElementChangeBySide(x, y, center_element, border_element,
14237 				 CE_TOUCHING_X, border_side);
14238 
14239 #if USE_FIX_CE_ACTION_WITH_PLAYER
14240     if (IS_PLAYER(xx, yy))
14241     {
14242       /* use player element that is initially defined in the level playfield,
14243 	 not the player element that corresponds to the runtime player number
14244 	 (example: a level that contains EL_PLAYER_3 as the only player would
14245 	 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14246       int player_element = PLAYERINFO(xx, yy)->initial_element;
14247 
14248       CheckElementChangeBySide(x, y, center_element, player_element,
14249 			       CE_TOUCHING_X, border_side);
14250     }
14251 #endif
14252   }
14253 }
14254 
14255 #else
14256 
TestIfElementTouchesCustomElement_OLD(int x,int y)14257 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14258 {
14259   static int xy[4][2] =
14260   {
14261     { 0, -1 },
14262     { -1, 0 },
14263     { +1, 0 },
14264     { 0, +1 }
14265   };
14266   static int trigger_sides[4][2] =
14267   {
14268     /* center side	border side */
14269     { CH_SIDE_TOP,	CH_SIDE_BOTTOM	},	/* check top    */
14270     { CH_SIDE_LEFT,	CH_SIDE_RIGHT	},	/* check left   */
14271     { CH_SIDE_RIGHT,	CH_SIDE_LEFT	},	/* check right  */
14272     { CH_SIDE_BOTTOM,	CH_SIDE_TOP	}	/* check bottom */
14273   };
14274   static int touch_dir[4] =
14275   {
14276     MV_LEFT | MV_RIGHT,
14277     MV_UP   | MV_DOWN,
14278     MV_UP   | MV_DOWN,
14279     MV_LEFT | MV_RIGHT
14280   };
14281   boolean change_center_element = FALSE;
14282   int center_element = Feld[x][y];	/* should always be non-moving! */
14283   int i;
14284 
14285   for (i = 0; i < NUM_DIRECTIONS; i++)
14286   {
14287     int xx = x + xy[i][0];
14288     int yy = y + xy[i][1];
14289     int center_side = trigger_sides[i][0];
14290     int border_side = trigger_sides[i][1];
14291     int border_element;
14292 
14293     if (!IN_LEV_FIELD(xx, yy))
14294       continue;
14295 
14296     if (game.engine_version < VERSION_IDENT(3,0,7,0))
14297       border_element = Feld[xx][yy];	/* may be moving! */
14298     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14299       border_element = Feld[xx][yy];
14300     else if (MovDir[xx][yy] & touch_dir[i])	/* elements are touching */
14301       border_element = MovingOrBlocked2Element(xx, yy);
14302     else
14303       continue;			/* center and border element do not touch */
14304 
14305     /* check for change of center element (but change it only once) */
14306     if (!change_center_element)
14307       change_center_element =
14308 	CheckElementChangeBySide(x, y, center_element, border_element,
14309 				 CE_TOUCHING_X, border_side);
14310 
14311     /* check for change of border element */
14312     CheckElementChangeBySide(xx, yy, border_element, center_element,
14313 			     CE_TOUCHING_X, center_side);
14314   }
14315 }
14316 
14317 #endif
14318 
TestIfElementHitsCustomElement(int x,int y,int direction)14319 void TestIfElementHitsCustomElement(int x, int y, int direction)
14320 {
14321   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14322   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14323   int hitx = x + dx, hity = y + dy;
14324   int hitting_element = Feld[x][y];
14325   int touched_element;
14326 
14327   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14328     return;
14329 
14330   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14331 		     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14332 
14333   if (IN_LEV_FIELD(hitx, hity))
14334   {
14335     int opposite_direction = MV_DIR_OPPOSITE(direction);
14336     int hitting_side = direction;
14337     int touched_side = opposite_direction;
14338     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14339 			  MovDir[hitx][hity] != direction ||
14340 			  ABS(MovPos[hitx][hity]) <= TILEY / 2);
14341 
14342     object_hit = TRUE;
14343 
14344     if (object_hit)
14345     {
14346       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14347 			       CE_HITTING_X, touched_side);
14348 
14349       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14350 			       CE_HIT_BY_X, hitting_side);
14351 
14352       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14353 			       CE_HIT_BY_SOMETHING, opposite_direction);
14354 
14355 #if USE_FIX_CE_ACTION_WITH_PLAYER
14356       if (IS_PLAYER(hitx, hity))
14357       {
14358 	/* use player element that is initially defined in the level playfield,
14359 	   not the player element that corresponds to the runtime player number
14360 	   (example: a level that contains EL_PLAYER_3 as the only player would
14361 	   incorrectly give EL_PLAYER_1 for "player->element_nr") */
14362 	int player_element = PLAYERINFO(hitx, hity)->initial_element;
14363 
14364 	CheckElementChangeBySide(x, y, hitting_element, player_element,
14365 				 CE_HITTING_X, touched_side);
14366       }
14367 #endif
14368     }
14369   }
14370 
14371   /* "hitting something" is also true when hitting the playfield border */
14372   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14373 			   CE_HITTING_SOMETHING, direction);
14374 }
14375 
14376 #if 0
14377 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14378 {
14379   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14380   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
14381   int hitx = x + dx, hity = y + dy;
14382   int hitting_element = Feld[x][y];
14383   int touched_element;
14384 #if 0
14385   boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14386 			!IS_FREE(hitx, hity) &&
14387 			(!IS_MOVING(hitx, hity) ||
14388 			 MovDir[hitx][hity] != direction ||
14389 			 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14390 #endif
14391 
14392   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14393     return;
14394 
14395 #if 0
14396   if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14397     return;
14398 #endif
14399 
14400   touched_element = (IN_LEV_FIELD(hitx, hity) ?
14401 		     MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14402 
14403   CheckElementChangeBySide(x, y, hitting_element, touched_element,
14404 			   EP_CAN_SMASH_EVERYTHING, direction);
14405 
14406   if (IN_LEV_FIELD(hitx, hity))
14407   {
14408     int opposite_direction = MV_DIR_OPPOSITE(direction);
14409     int hitting_side = direction;
14410     int touched_side = opposite_direction;
14411 #if 0
14412     int touched_element = MovingOrBlocked2Element(hitx, hity);
14413 #endif
14414 #if 1
14415     boolean object_hit = (!IS_MOVING(hitx, hity) ||
14416 			  MovDir[hitx][hity] != direction ||
14417 			  ABS(MovPos[hitx][hity]) <= TILEY / 2);
14418 
14419     object_hit = TRUE;
14420 #endif
14421 
14422     if (object_hit)
14423     {
14424       int i;
14425 
14426       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14427 			       CE_SMASHED_BY_SOMETHING, opposite_direction);
14428 
14429       CheckElementChangeBySide(x, y, hitting_element, touched_element,
14430 			       CE_OTHER_IS_SMASHING, touched_side);
14431 
14432       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14433 			       CE_OTHER_GETS_SMASHED, hitting_side);
14434     }
14435   }
14436 }
14437 #endif
14438 
TestIfGoodThingHitsBadThing(int good_x,int good_y,int good_move_dir)14439 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14440 {
14441   int i, kill_x = -1, kill_y = -1;
14442 
14443   int bad_element = -1;
14444   static int test_xy[4][2] =
14445   {
14446     { 0, -1 },
14447     { -1, 0 },
14448     { +1, 0 },
14449     { 0, +1 }
14450   };
14451   static int test_dir[4] =
14452   {
14453     MV_UP,
14454     MV_LEFT,
14455     MV_RIGHT,
14456     MV_DOWN
14457   };
14458 
14459   for (i = 0; i < NUM_DIRECTIONS; i++)
14460   {
14461     int test_x, test_y, test_move_dir, test_element;
14462 
14463     test_x = good_x + test_xy[i][0];
14464     test_y = good_y + test_xy[i][1];
14465 
14466     if (!IN_LEV_FIELD(test_x, test_y))
14467       continue;
14468 
14469     test_move_dir =
14470       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14471 
14472     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14473 
14474     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14475        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14476     */
14477     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14478 	(DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14479     {
14480       kill_x = test_x;
14481       kill_y = test_y;
14482       bad_element = test_element;
14483 
14484       break;
14485     }
14486   }
14487 
14488   if (kill_x != -1 || kill_y != -1)
14489   {
14490     if (IS_PLAYER(good_x, good_y))
14491     {
14492       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14493 
14494       if (player->shield_deadly_time_left > 0 &&
14495 	  !IS_INDESTRUCTIBLE(bad_element))
14496 	Bang(kill_x, kill_y);
14497       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14498 	KillPlayer(player);
14499     }
14500     else
14501       Bang(good_x, good_y);
14502   }
14503 }
14504 
TestIfBadThingHitsGoodThing(int bad_x,int bad_y,int bad_move_dir)14505 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14506 {
14507   int i, kill_x = -1, kill_y = -1;
14508   int bad_element = Feld[bad_x][bad_y];
14509   static int test_xy[4][2] =
14510   {
14511     { 0, -1 },
14512     { -1, 0 },
14513     { +1, 0 },
14514     { 0, +1 }
14515   };
14516   static int touch_dir[4] =
14517   {
14518     MV_LEFT | MV_RIGHT,
14519     MV_UP   | MV_DOWN,
14520     MV_UP   | MV_DOWN,
14521     MV_LEFT | MV_RIGHT
14522   };
14523   static int test_dir[4] =
14524   {
14525     MV_UP,
14526     MV_LEFT,
14527     MV_RIGHT,
14528     MV_DOWN
14529   };
14530 
14531   if (bad_element == EL_EXPLOSION)	/* skip just exploding bad things */
14532     return;
14533 
14534   for (i = 0; i < NUM_DIRECTIONS; i++)
14535   {
14536     int test_x, test_y, test_move_dir, test_element;
14537 
14538     test_x = bad_x + test_xy[i][0];
14539     test_y = bad_y + test_xy[i][1];
14540 
14541     if (!IN_LEV_FIELD(test_x, test_y))
14542       continue;
14543 
14544     test_move_dir =
14545       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14546 
14547     test_element = Feld[test_x][test_y];
14548 
14549     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14550        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14551     */
14552     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14553 	(DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14554     {
14555       /* good thing is player or penguin that does not move away */
14556       if (IS_PLAYER(test_x, test_y))
14557       {
14558 	struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14559 
14560 	if (bad_element == EL_ROBOT && player->is_moving)
14561 	  continue;	/* robot does not kill player if he is moving */
14562 
14563 	if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14564 	{
14565 	  if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14566 	    continue;		/* center and border element do not touch */
14567 	}
14568 
14569 	kill_x = test_x;
14570 	kill_y = test_y;
14571 
14572 	break;
14573       }
14574       else if (test_element == EL_PENGUIN)
14575       {
14576 	kill_x = test_x;
14577 	kill_y = test_y;
14578 
14579 	break;
14580       }
14581     }
14582   }
14583 
14584   if (kill_x != -1 || kill_y != -1)
14585   {
14586     if (IS_PLAYER(kill_x, kill_y))
14587     {
14588       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14589 
14590       if (player->shield_deadly_time_left > 0 &&
14591 	  !IS_INDESTRUCTIBLE(bad_element))
14592 	Bang(bad_x, bad_y);
14593       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14594 	KillPlayer(player);
14595     }
14596     else
14597       Bang(kill_x, kill_y);
14598   }
14599 }
14600 
TestIfGoodThingGetsHitByBadThing(int bad_x,int bad_y,int bad_move_dir)14601 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14602 {
14603   int bad_element = Feld[bad_x][bad_y];
14604   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14605   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14606   int test_x = bad_x + dx, test_y = bad_y + dy;
14607   int test_move_dir, test_element;
14608   int kill_x = -1, kill_y = -1;
14609 
14610   if (!IN_LEV_FIELD(test_x, test_y))
14611     return;
14612 
14613   test_move_dir =
14614     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14615 
14616   test_element = Feld[test_x][test_y];
14617 
14618   if (test_move_dir != bad_move_dir)
14619   {
14620     /* good thing can be player or penguin that does not move away */
14621     if (IS_PLAYER(test_x, test_y))
14622     {
14623       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14624 
14625       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14626 	 player as being hit when he is moving towards the bad thing, because
14627 	 the "get hit by" condition would be lost after the player stops) */
14628       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14629 	return;		/* player moves away from bad thing */
14630 
14631       kill_x = test_x;
14632       kill_y = test_y;
14633     }
14634     else if (test_element == EL_PENGUIN)
14635     {
14636       kill_x = test_x;
14637       kill_y = test_y;
14638     }
14639   }
14640 
14641   if (kill_x != -1 || kill_y != -1)
14642   {
14643     if (IS_PLAYER(kill_x, kill_y))
14644     {
14645       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14646 
14647       if (player->shield_deadly_time_left > 0 &&
14648 	  !IS_INDESTRUCTIBLE(bad_element))
14649 	Bang(bad_x, bad_y);
14650       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14651 	KillPlayer(player);
14652     }
14653     else
14654       Bang(kill_x, kill_y);
14655   }
14656 }
14657 
TestIfPlayerTouchesBadThing(int x,int y)14658 void TestIfPlayerTouchesBadThing(int x, int y)
14659 {
14660   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14661 }
14662 
TestIfPlayerRunsIntoBadThing(int x,int y,int move_dir)14663 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14664 {
14665   TestIfGoodThingHitsBadThing(x, y, move_dir);
14666 }
14667 
TestIfBadThingTouchesPlayer(int x,int y)14668 void TestIfBadThingTouchesPlayer(int x, int y)
14669 {
14670   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14671 }
14672 
TestIfBadThingRunsIntoPlayer(int x,int y,int move_dir)14673 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14674 {
14675   TestIfBadThingHitsGoodThing(x, y, move_dir);
14676 }
14677 
TestIfFriendTouchesBadThing(int x,int y)14678 void TestIfFriendTouchesBadThing(int x, int y)
14679 {
14680   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14681 }
14682 
TestIfBadThingTouchesFriend(int x,int y)14683 void TestIfBadThingTouchesFriend(int x, int y)
14684 {
14685   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14686 }
14687 
TestIfBadThingTouchesOtherBadThing(int bad_x,int bad_y)14688 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14689 {
14690   int i, kill_x = bad_x, kill_y = bad_y;
14691   static int xy[4][2] =
14692   {
14693     { 0, -1 },
14694     { -1, 0 },
14695     { +1, 0 },
14696     { 0, +1 }
14697   };
14698 
14699   for (i = 0; i < NUM_DIRECTIONS; i++)
14700   {
14701     int x, y, element;
14702 
14703     x = bad_x + xy[i][0];
14704     y = bad_y + xy[i][1];
14705     if (!IN_LEV_FIELD(x, y))
14706       continue;
14707 
14708     element = Feld[x][y];
14709     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14710 	element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14711     {
14712       kill_x = x;
14713       kill_y = y;
14714       break;
14715     }
14716   }
14717 
14718   if (kill_x != bad_x || kill_y != bad_y)
14719     Bang(bad_x, bad_y);
14720 }
14721 
KillPlayer(struct PlayerInfo * player)14722 void KillPlayer(struct PlayerInfo *player)
14723 {
14724   int jx = player->jx, jy = player->jy;
14725 
14726   if (!player->active)
14727     return;
14728 
14729 #if 0
14730   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14731 	 player->killed, player->active, player->reanimated);
14732 #endif
14733 
14734   /* the following code was introduced to prevent an infinite loop when calling
14735      -> Bang()
14736      -> CheckTriggeredElementChangeExt()
14737      -> ExecuteCustomElementAction()
14738      -> KillPlayer()
14739      -> (infinitely repeating the above sequence of function calls)
14740      which occurs when killing the player while having a CE with the setting
14741      "kill player X when explosion of <player X>"; the solution using a new
14742      field "player->killed" was chosen for backwards compatibility, although
14743      clever use of the fields "player->active" etc. would probably also work */
14744 #if 1
14745   if (player->killed)
14746     return;
14747 #endif
14748 
14749   player->killed = TRUE;
14750 
14751   /* remove accessible field at the player's position */
14752   Feld[jx][jy] = EL_EMPTY;
14753 
14754   /* deactivate shield (else Bang()/Explode() would not work right) */
14755   player->shield_normal_time_left = 0;
14756   player->shield_deadly_time_left = 0;
14757 
14758 #if 0
14759   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14760 	 player->killed, player->active, player->reanimated);
14761 #endif
14762 
14763   Bang(jx, jy);
14764 
14765 #if 0
14766   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14767 	 player->killed, player->active, player->reanimated);
14768 #endif
14769 
14770 #if USE_PLAYER_REANIMATION
14771 #if 1
14772   if (player->reanimated)	/* killed player may have been reanimated */
14773     player->killed = player->reanimated = FALSE;
14774   else
14775     BuryPlayer(player);
14776 #else
14777   if (player->killed)		/* player may have been reanimated */
14778     BuryPlayer(player);
14779 #endif
14780 #else
14781   BuryPlayer(player);
14782 #endif
14783 }
14784 
KillPlayerUnlessEnemyProtected(int x,int y)14785 static void KillPlayerUnlessEnemyProtected(int x, int y)
14786 {
14787   if (!PLAYER_ENEMY_PROTECTED(x, y))
14788     KillPlayer(PLAYERINFO(x, y));
14789 }
14790 
KillPlayerUnlessExplosionProtected(int x,int y)14791 static void KillPlayerUnlessExplosionProtected(int x, int y)
14792 {
14793   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14794     KillPlayer(PLAYERINFO(x, y));
14795 }
14796 
BuryPlayer(struct PlayerInfo * player)14797 void BuryPlayer(struct PlayerInfo *player)
14798 {
14799   int jx = player->jx, jy = player->jy;
14800 
14801   if (!player->active)
14802     return;
14803 
14804   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14805   PlayLevelSound(jx, jy, SND_GAME_LOSING);
14806 
14807   player->GameOver = TRUE;
14808   RemovePlayer(player);
14809 }
14810 
RemovePlayer(struct PlayerInfo * player)14811 void RemovePlayer(struct PlayerInfo *player)
14812 {
14813   int jx = player->jx, jy = player->jy;
14814   int i, found = FALSE;
14815 
14816   player->present = FALSE;
14817   player->active = FALSE;
14818 
14819   if (!ExplodeField[jx][jy])
14820     StorePlayer[jx][jy] = 0;
14821 
14822   if (player->is_moving)
14823     TEST_DrawLevelField(player->last_jx, player->last_jy);
14824 
14825   for (i = 0; i < MAX_PLAYERS; i++)
14826     if (stored_player[i].active)
14827       found = TRUE;
14828 
14829   if (!found)
14830     AllPlayersGone = TRUE;
14831 
14832   ExitX = ZX = jx;
14833   ExitY = ZY = jy;
14834 }
14835 
14836 #if USE_NEW_SNAP_DELAY
setFieldForSnapping(int x,int y,int element,int direction)14837 static void setFieldForSnapping(int x, int y, int element, int direction)
14838 {
14839   struct ElementInfo *ei = &element_info[element];
14840   int direction_bit = MV_DIR_TO_BIT(direction);
14841   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14842   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14843 		IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14844 
14845   Feld[x][y] = EL_ELEMENT_SNAPPING;
14846   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14847 
14848   ResetGfxAnimation(x, y);
14849 
14850   GfxElement[x][y] = element;
14851   GfxAction[x][y] = action;
14852   GfxDir[x][y] = direction;
14853   GfxFrame[x][y] = -1;
14854 }
14855 #endif
14856 
14857 /*
14858   =============================================================================
14859   checkDiagonalPushing()
14860   -----------------------------------------------------------------------------
14861   check if diagonal input device direction results in pushing of object
14862   (by checking if the alternative direction is walkable, diggable, ...)
14863   =============================================================================
14864 */
14865 
checkDiagonalPushing(struct PlayerInfo * player,int x,int y,int real_dx,int real_dy)14866 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14867 				    int x, int y, int real_dx, int real_dy)
14868 {
14869   int jx, jy, dx, dy, xx, yy;
14870 
14871   if (real_dx == 0 || real_dy == 0)	/* no diagonal direction => push */
14872     return TRUE;
14873 
14874   /* diagonal direction: check alternative direction */
14875   jx = player->jx;
14876   jy = player->jy;
14877   dx = x - jx;
14878   dy = y - jy;
14879   xx = jx + (dx == 0 ? real_dx : 0);
14880   yy = jy + (dy == 0 ? real_dy : 0);
14881 
14882   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14883 }
14884 
14885 /*
14886   =============================================================================
14887   DigField()
14888   -----------------------------------------------------------------------------
14889   x, y:			field next to player (non-diagonal) to try to dig to
14890   real_dx, real_dy:	direction as read from input device (can be diagonal)
14891   =============================================================================
14892 */
14893 
DigField(struct PlayerInfo * player,int oldx,int oldy,int x,int y,int real_dx,int real_dy,int mode)14894 static int DigField(struct PlayerInfo *player,
14895 		    int oldx, int oldy, int x, int y,
14896 		    int real_dx, int real_dy, int mode)
14897 {
14898   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14899   boolean player_was_pushing = player->is_pushing;
14900   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14901   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14902   int jx = oldx, jy = oldy;
14903   int dx = x - jx, dy = y - jy;
14904   int nextx = x + dx, nexty = y + dy;
14905   int move_direction = (dx == -1 ? MV_LEFT  :
14906 			dx == +1 ? MV_RIGHT :
14907 			dy == -1 ? MV_UP    :
14908 			dy == +1 ? MV_DOWN  : MV_NONE);
14909   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14910   int dig_side = MV_DIR_OPPOSITE(move_direction);
14911   int old_element = Feld[jx][jy];
14912 #if USE_FIXED_DONT_RUN_INTO
14913   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14914 #else
14915   int element;
14916 #endif
14917   int collect_count;
14918 
14919   if (is_player)		/* function can also be called by EL_PENGUIN */
14920   {
14921     if (player->MovPos == 0)
14922     {
14923       player->is_digging = FALSE;
14924       player->is_collecting = FALSE;
14925     }
14926 
14927     if (player->MovPos == 0)	/* last pushing move finished */
14928       player->is_pushing = FALSE;
14929 
14930     if (mode == DF_NO_PUSH)	/* player just stopped pushing */
14931     {
14932       player->is_switching = FALSE;
14933       player->push_delay = -1;
14934 
14935       return MP_NO_ACTION;
14936     }
14937   }
14938 
14939 #if !USE_FIXED_DONT_RUN_INTO
14940   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14941     return MP_NO_ACTION;
14942 #endif
14943 
14944   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14945     old_element = Back[jx][jy];
14946 
14947   /* in case of element dropped at player position, check background */
14948   else if (Back[jx][jy] != EL_EMPTY &&
14949 	   game.engine_version >= VERSION_IDENT(2,2,0,0))
14950     old_element = Back[jx][jy];
14951 
14952   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14953     return MP_NO_ACTION;	/* field has no opening in this direction */
14954 
14955   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14956     return MP_NO_ACTION;	/* field has no opening in this direction */
14957 
14958 #if USE_FIXED_DONT_RUN_INTO
14959   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14960   {
14961     SplashAcid(x, y);
14962 
14963     Feld[jx][jy] = player->artwork_element;
14964     InitMovingField(jx, jy, MV_DOWN);
14965     Store[jx][jy] = EL_ACID;
14966     ContinueMoving(jx, jy);
14967     BuryPlayer(player);
14968 
14969     return MP_DONT_RUN_INTO;
14970   }
14971 #endif
14972 
14973 #if USE_FIXED_DONT_RUN_INTO
14974   if (player_can_move && DONT_RUN_INTO(element))
14975   {
14976     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14977 
14978     return MP_DONT_RUN_INTO;
14979   }
14980 #endif
14981 
14982 #if USE_FIXED_DONT_RUN_INTO
14983   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14984     return MP_NO_ACTION;
14985 #endif
14986 
14987 #if !USE_FIXED_DONT_RUN_INTO
14988   element = Feld[x][y];
14989 #endif
14990 
14991   collect_count = element_info[element].collect_count_initial;
14992 
14993   if (!is_player && !IS_COLLECTIBLE(element))	/* penguin cannot collect it */
14994     return MP_NO_ACTION;
14995 
14996   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14997     player_can_move = player_can_move_or_snap;
14998 
14999   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
15000       game.engine_version >= VERSION_IDENT(2,2,0,0))
15001   {
15002     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
15003 			       player->index_bit, dig_side);
15004     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15005 					player->index_bit, dig_side);
15006 
15007     if (element == EL_DC_LANDMINE)
15008       Bang(x, y);
15009 
15010     if (Feld[x][y] != element)		/* field changed by snapping */
15011       return MP_ACTION;
15012 
15013     return MP_NO_ACTION;
15014   }
15015 
15016 #if USE_PLAYER_GRAVITY
15017   if (player->gravity && is_player && !player->is_auto_moving &&
15018       canFallDown(player) && move_direction != MV_DOWN &&
15019       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15020     return MP_NO_ACTION;	/* player cannot walk here due to gravity */
15021 #else
15022   if (game.gravity && is_player && !player->is_auto_moving &&
15023       canFallDown(player) && move_direction != MV_DOWN &&
15024       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
15025     return MP_NO_ACTION;	/* player cannot walk here due to gravity */
15026 #endif
15027 
15028   if (player_can_move &&
15029       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
15030   {
15031     int sound_element = SND_ELEMENT(element);
15032     int sound_action = ACTION_WALKING;
15033 
15034     if (IS_RND_GATE(element))
15035     {
15036       if (!player->key[RND_GATE_NR(element)])
15037 	return MP_NO_ACTION;
15038     }
15039     else if (IS_RND_GATE_GRAY(element))
15040     {
15041       if (!player->key[RND_GATE_GRAY_NR(element)])
15042 	return MP_NO_ACTION;
15043     }
15044     else if (IS_RND_GATE_GRAY_ACTIVE(element))
15045     {
15046       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
15047 	return MP_NO_ACTION;
15048     }
15049     else if (element == EL_EXIT_OPEN ||
15050 	     element == EL_EM_EXIT_OPEN ||
15051 #if 1
15052 	     element == EL_EM_EXIT_OPENING ||
15053 #endif
15054 	     element == EL_STEEL_EXIT_OPEN ||
15055 	     element == EL_EM_STEEL_EXIT_OPEN ||
15056 #if 1
15057 	     element == EL_EM_STEEL_EXIT_OPENING ||
15058 #endif
15059 	     element == EL_SP_EXIT_OPEN ||
15060 	     element == EL_SP_EXIT_OPENING)
15061     {
15062       sound_action = ACTION_PASSING;	/* player is passing exit */
15063     }
15064     else if (element == EL_EMPTY)
15065     {
15066       sound_action = ACTION_MOVING;		/* nothing to walk on */
15067     }
15068 
15069     /* play sound from background or player, whatever is available */
15070     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
15071       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
15072     else
15073       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
15074   }
15075   else if (player_can_move &&
15076 	   IS_PASSABLE(element) && canPassField(x, y, move_direction))
15077   {
15078     if (!ACCESS_FROM(element, opposite_direction))
15079       return MP_NO_ACTION;	/* field not accessible from this direction */
15080 
15081     if (CAN_MOVE(element))	/* only fixed elements can be passed! */
15082       return MP_NO_ACTION;
15083 
15084     if (IS_EM_GATE(element))
15085     {
15086       if (!player->key[EM_GATE_NR(element)])
15087 	return MP_NO_ACTION;
15088     }
15089     else if (IS_EM_GATE_GRAY(element))
15090     {
15091       if (!player->key[EM_GATE_GRAY_NR(element)])
15092 	return MP_NO_ACTION;
15093     }
15094     else if (IS_EM_GATE_GRAY_ACTIVE(element))
15095     {
15096       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
15097 	return MP_NO_ACTION;
15098     }
15099     else if (IS_EMC_GATE(element))
15100     {
15101       if (!player->key[EMC_GATE_NR(element)])
15102 	return MP_NO_ACTION;
15103     }
15104     else if (IS_EMC_GATE_GRAY(element))
15105     {
15106       if (!player->key[EMC_GATE_GRAY_NR(element)])
15107 	return MP_NO_ACTION;
15108     }
15109     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
15110     {
15111       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15112 	return MP_NO_ACTION;
15113     }
15114     else if (element == EL_DC_GATE_WHITE ||
15115 	     element == EL_DC_GATE_WHITE_GRAY ||
15116 	     element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15117     {
15118       if (player->num_white_keys == 0)
15119 	return MP_NO_ACTION;
15120 
15121       player->num_white_keys--;
15122     }
15123     else if (IS_SP_PORT(element))
15124     {
15125       if (element == EL_SP_GRAVITY_PORT_LEFT ||
15126 	  element == EL_SP_GRAVITY_PORT_RIGHT ||
15127 	  element == EL_SP_GRAVITY_PORT_UP ||
15128 	  element == EL_SP_GRAVITY_PORT_DOWN)
15129 #if USE_PLAYER_GRAVITY
15130 	player->gravity = !player->gravity;
15131 #else
15132 	game.gravity = !game.gravity;
15133 #endif
15134       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15135 	       element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15136 	       element == EL_SP_GRAVITY_ON_PORT_UP ||
15137 	       element == EL_SP_GRAVITY_ON_PORT_DOWN)
15138 #if USE_PLAYER_GRAVITY
15139 	player->gravity = TRUE;
15140 #else
15141 	game.gravity = TRUE;
15142 #endif
15143       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15144 	       element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15145 	       element == EL_SP_GRAVITY_OFF_PORT_UP ||
15146 	       element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15147 #if USE_PLAYER_GRAVITY
15148 	player->gravity = FALSE;
15149 #else
15150 	game.gravity = FALSE;
15151 #endif
15152     }
15153 
15154     /* automatically move to the next field with double speed */
15155     player->programmed_action = move_direction;
15156 
15157     if (player->move_delay_reset_counter == 0)
15158     {
15159       player->move_delay_reset_counter = 2;	/* two double speed steps */
15160 
15161       DOUBLE_PLAYER_SPEED(player);
15162     }
15163 
15164     PlayLevelSoundAction(x, y, ACTION_PASSING);
15165   }
15166   else if (player_can_move_or_snap && IS_DIGGABLE(element))
15167   {
15168     RemoveField(x, y);
15169 
15170     if (mode != DF_SNAP)
15171     {
15172       GfxElement[x][y] = GFX_ELEMENT(element);
15173       player->is_digging = TRUE;
15174     }
15175 
15176     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15177 
15178     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15179 					player->index_bit, dig_side);
15180 
15181     if (mode == DF_SNAP)
15182     {
15183 #if USE_NEW_SNAP_DELAY
15184       if (level.block_snap_field)
15185 	setFieldForSnapping(x, y, element, move_direction);
15186       else
15187 	TestIfElementTouchesCustomElement(x, y);	/* for empty space */
15188 #else
15189       TestIfElementTouchesCustomElement(x, y);		/* for empty space */
15190 #endif
15191 
15192       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15193 					  player->index_bit, dig_side);
15194     }
15195   }
15196   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15197   {
15198     RemoveField(x, y);
15199 
15200     if (is_player && mode != DF_SNAP)
15201     {
15202       GfxElement[x][y] = element;
15203       player->is_collecting = TRUE;
15204     }
15205 
15206     if (element == EL_SPEED_PILL)
15207     {
15208       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15209     }
15210     else if (element == EL_EXTRA_TIME && level.time > 0)
15211     {
15212       TimeLeft += level.extra_time;
15213 
15214 #if 1
15215       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15216 
15217       DisplayGameControlValues();
15218 #else
15219       DrawGameValue_Time(TimeLeft);
15220 #endif
15221     }
15222     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15223     {
15224       player->shield_normal_time_left += level.shield_normal_time;
15225       if (element == EL_SHIELD_DEADLY)
15226 	player->shield_deadly_time_left += level.shield_deadly_time;
15227     }
15228     else if (element == EL_DYNAMITE ||
15229 	     element == EL_EM_DYNAMITE ||
15230 	     element == EL_SP_DISK_RED)
15231     {
15232       if (player->inventory_size < MAX_INVENTORY_SIZE)
15233 	player->inventory_element[player->inventory_size++] = element;
15234 
15235       DrawGameDoorValues();
15236     }
15237     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15238     {
15239       player->dynabomb_count++;
15240       player->dynabombs_left++;
15241     }
15242     else if (element == EL_DYNABOMB_INCREASE_SIZE)
15243     {
15244       player->dynabomb_size++;
15245     }
15246     else if (element == EL_DYNABOMB_INCREASE_POWER)
15247     {
15248       player->dynabomb_xl = TRUE;
15249     }
15250     else if (IS_KEY(element))
15251     {
15252       player->key[KEY_NR(element)] = TRUE;
15253 
15254       DrawGameDoorValues();
15255     }
15256     else if (element == EL_DC_KEY_WHITE)
15257     {
15258       player->num_white_keys++;
15259 
15260       /* display white keys? */
15261       /* DrawGameDoorValues(); */
15262     }
15263     else if (IS_ENVELOPE(element))
15264     {
15265       player->show_envelope = element;
15266     }
15267     else if (element == EL_EMC_LENSES)
15268     {
15269       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15270 
15271       RedrawAllInvisibleElementsForLenses();
15272     }
15273     else if (element == EL_EMC_MAGNIFIER)
15274     {
15275       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15276 
15277       RedrawAllInvisibleElementsForMagnifier();
15278     }
15279     else if (IS_DROPPABLE(element) ||
15280 	     IS_THROWABLE(element))	/* can be collected and dropped */
15281     {
15282       int i;
15283 
15284       if (collect_count == 0)
15285 	player->inventory_infinite_element = element;
15286       else
15287 	for (i = 0; i < collect_count; i++)
15288 	  if (player->inventory_size < MAX_INVENTORY_SIZE)
15289 	    player->inventory_element[player->inventory_size++] = element;
15290 
15291       DrawGameDoorValues();
15292     }
15293     else if (collect_count > 0)
15294     {
15295       local_player->gems_still_needed -= collect_count;
15296       if (local_player->gems_still_needed < 0)
15297 	local_player->gems_still_needed = 0;
15298 
15299 #if 1
15300       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15301 
15302       DisplayGameControlValues();
15303 #else
15304       DrawGameValue_Emeralds(local_player->gems_still_needed);
15305 #endif
15306     }
15307 
15308     RaiseScoreElement(element);
15309     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15310 
15311     if (is_player)
15312       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15313 					  player->index_bit, dig_side);
15314 
15315     if (mode == DF_SNAP)
15316     {
15317 #if USE_NEW_SNAP_DELAY
15318       if (level.block_snap_field)
15319 	setFieldForSnapping(x, y, element, move_direction);
15320       else
15321 	TestIfElementTouchesCustomElement(x, y);	/* for empty space */
15322 #else
15323       TestIfElementTouchesCustomElement(x, y);		/* for empty space */
15324 #endif
15325 
15326       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15327 					  player->index_bit, dig_side);
15328     }
15329   }
15330   else if (player_can_move_or_snap && IS_PUSHABLE(element))
15331   {
15332     if (mode == DF_SNAP && element != EL_BD_ROCK)
15333       return MP_NO_ACTION;
15334 
15335     if (CAN_FALL(element) && dy)
15336       return MP_NO_ACTION;
15337 
15338     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15339 	!(element == EL_SPRING && level.use_spring_bug))
15340       return MP_NO_ACTION;
15341 
15342     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15343 	((move_direction & MV_VERTICAL &&
15344 	  ((element_info[element].move_pattern & MV_LEFT &&
15345 	    IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15346 	   (element_info[element].move_pattern & MV_RIGHT &&
15347 	    IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15348 	 (move_direction & MV_HORIZONTAL &&
15349 	  ((element_info[element].move_pattern & MV_UP &&
15350 	    IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15351 	   (element_info[element].move_pattern & MV_DOWN &&
15352 	    IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15353       return MP_NO_ACTION;
15354 
15355     /* do not push elements already moving away faster than player */
15356     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15357 	ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15358       return MP_NO_ACTION;
15359 
15360     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15361     {
15362       if (player->push_delay_value == -1 || !player_was_pushing)
15363 	player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15364     }
15365     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15366     {
15367       if (player->push_delay_value == -1)
15368 	player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15369     }
15370     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15371     {
15372       if (!player->is_pushing)
15373 	player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15374     }
15375 
15376     player->is_pushing = TRUE;
15377     player->is_active = TRUE;
15378 
15379     if (!(IN_LEV_FIELD(nextx, nexty) &&
15380 	  (IS_FREE(nextx, nexty) ||
15381 	   (IS_SB_ELEMENT(element) &&
15382 	    Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15383 	   (IS_CUSTOM_ELEMENT(element) &&
15384 	    CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15385       return MP_NO_ACTION;
15386 
15387     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15388       return MP_NO_ACTION;
15389 
15390     if (player->push_delay == -1)	/* new pushing; restart delay */
15391       player->push_delay = 0;
15392 
15393     if (player->push_delay < player->push_delay_value &&
15394 	!(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15395 	element != EL_SPRING && element != EL_BALLOON)
15396     {
15397       /* make sure that there is no move delay before next try to push */
15398       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15399 	player->move_delay = 0;
15400 
15401       return MP_NO_ACTION;
15402     }
15403 
15404     if (IS_CUSTOM_ELEMENT(element) &&
15405 	CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15406     {
15407       if (!DigFieldByCE(nextx, nexty, element))
15408 	return MP_NO_ACTION;
15409     }
15410 
15411     if (IS_SB_ELEMENT(element))
15412     {
15413       if (element == EL_SOKOBAN_FIELD_FULL)
15414       {
15415 	Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15416 	local_player->sokobanfields_still_needed++;
15417       }
15418 
15419       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15420       {
15421 	Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15422 	local_player->sokobanfields_still_needed--;
15423       }
15424 
15425       Feld[x][y] = EL_SOKOBAN_OBJECT;
15426 
15427       if (Back[x][y] == Back[nextx][nexty])
15428 	PlayLevelSoundAction(x, y, ACTION_PUSHING);
15429       else if (Back[x][y] != 0)
15430 	PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15431 				    ACTION_EMPTYING);
15432       else
15433 	PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15434 				    ACTION_FILLING);
15435 
15436 #if 1
15437       if (local_player->sokobanfields_still_needed == 0 &&
15438 	  (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
15439 #else
15440       if (local_player->sokobanfields_still_needed == 0 &&
15441 	  game.emulation == EMU_SOKOBAN)
15442 #endif
15443       {
15444 	PlayerWins(player);
15445 
15446 	PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15447       }
15448     }
15449     else
15450       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15451 
15452     InitMovingField(x, y, move_direction);
15453     GfxAction[x][y] = ACTION_PUSHING;
15454 
15455     if (mode == DF_SNAP)
15456       ContinueMoving(x, y);
15457     else
15458       MovPos[x][y] = (dx != 0 ? dx : dy);
15459 
15460     Pushed[x][y] = TRUE;
15461     Pushed[nextx][nexty] = TRUE;
15462 
15463     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15464       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15465     else
15466       player->push_delay_value = -1;	/* get new value later */
15467 
15468     /* check for element change _after_ element has been pushed */
15469     if (game.use_change_when_pushing_bug)
15470     {
15471       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15472 				 player->index_bit, dig_side);
15473       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15474 					  player->index_bit, dig_side);
15475     }
15476   }
15477   else if (IS_SWITCHABLE(element))
15478   {
15479     if (PLAYER_SWITCHING(player, x, y))
15480     {
15481       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15482 					  player->index_bit, dig_side);
15483 
15484       return MP_ACTION;
15485     }
15486 
15487     player->is_switching = TRUE;
15488     player->switch_x = x;
15489     player->switch_y = y;
15490 
15491     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15492 
15493     if (element == EL_ROBOT_WHEEL)
15494     {
15495       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15496       ZX = x;
15497       ZY = y;
15498 
15499       game.robot_wheel_active = TRUE;
15500 
15501       TEST_DrawLevelField(x, y);
15502     }
15503     else if (element == EL_SP_TERMINAL)
15504     {
15505       int xx, yy;
15506 
15507       SCAN_PLAYFIELD(xx, yy)
15508       {
15509 	if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15510 	  Bang(xx, yy);
15511 	else if (Feld[xx][yy] == EL_SP_TERMINAL)
15512 	  Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15513       }
15514     }
15515     else if (IS_BELT_SWITCH(element))
15516     {
15517       ToggleBeltSwitch(x, y);
15518     }
15519     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15520 	     element == EL_SWITCHGATE_SWITCH_DOWN ||
15521 	     element == EL_DC_SWITCHGATE_SWITCH_UP ||
15522 	     element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15523     {
15524       ToggleSwitchgateSwitch(x, y);
15525     }
15526     else if (element == EL_LIGHT_SWITCH ||
15527 	     element == EL_LIGHT_SWITCH_ACTIVE)
15528     {
15529       ToggleLightSwitch(x, y);
15530     }
15531     else if (element == EL_TIMEGATE_SWITCH ||
15532 	     element == EL_DC_TIMEGATE_SWITCH)
15533     {
15534       ActivateTimegateSwitch(x, y);
15535     }
15536     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15537 	     element == EL_BALLOON_SWITCH_RIGHT ||
15538 	     element == EL_BALLOON_SWITCH_UP    ||
15539 	     element == EL_BALLOON_SWITCH_DOWN  ||
15540 	     element == EL_BALLOON_SWITCH_NONE  ||
15541 	     element == EL_BALLOON_SWITCH_ANY)
15542     {
15543       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15544 			     element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15545 			     element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15546 			     element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15547 			     element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15548 			     move_direction);
15549     }
15550     else if (element == EL_LAMP)
15551     {
15552       Feld[x][y] = EL_LAMP_ACTIVE;
15553       local_player->lights_still_needed--;
15554 
15555       ResetGfxAnimation(x, y);
15556       TEST_DrawLevelField(x, y);
15557     }
15558     else if (element == EL_TIME_ORB_FULL)
15559     {
15560       Feld[x][y] = EL_TIME_ORB_EMPTY;
15561 
15562       if (level.time > 0 || level.use_time_orb_bug)
15563       {
15564 	TimeLeft += level.time_orb_time;
15565 
15566 #if 1
15567 	game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15568 
15569 	DisplayGameControlValues();
15570 #else
15571 	DrawGameValue_Time(TimeLeft);
15572 #endif
15573       }
15574 
15575       ResetGfxAnimation(x, y);
15576       TEST_DrawLevelField(x, y);
15577     }
15578     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15579 	     element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15580     {
15581       int xx, yy;
15582 
15583       game.ball_state = !game.ball_state;
15584 
15585       SCAN_PLAYFIELD(xx, yy)
15586       {
15587 	int e = Feld[xx][yy];
15588 
15589 	if (game.ball_state)
15590 	{
15591 	  if (e == EL_EMC_MAGIC_BALL)
15592 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15593 	  else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15594 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15595 	}
15596 	else
15597 	{
15598 	  if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15599 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15600 	  else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15601 	    CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15602 	}
15603       }
15604     }
15605 
15606     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15607 					player->index_bit, dig_side);
15608 
15609     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15610 					player->index_bit, dig_side);
15611 
15612     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15613 					player->index_bit, dig_side);
15614 
15615     return MP_ACTION;
15616   }
15617   else
15618   {
15619     if (!PLAYER_SWITCHING(player, x, y))
15620     {
15621       player->is_switching = TRUE;
15622       player->switch_x = x;
15623       player->switch_y = y;
15624 
15625       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15626 				 player->index_bit, dig_side);
15627       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15628 					  player->index_bit, dig_side);
15629 
15630       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15631 				 player->index_bit, dig_side);
15632       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15633 					  player->index_bit, dig_side);
15634     }
15635 
15636     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15637 			       player->index_bit, dig_side);
15638     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15639 					player->index_bit, dig_side);
15640 
15641     return MP_NO_ACTION;
15642   }
15643 
15644   player->push_delay = -1;
15645 
15646   if (is_player)		/* function can also be called by EL_PENGUIN */
15647   {
15648     if (Feld[x][y] != element)		/* really digged/collected something */
15649     {
15650       player->is_collecting = !player->is_digging;
15651       player->is_active = TRUE;
15652     }
15653   }
15654 
15655   return MP_MOVING;
15656 }
15657 
DigFieldByCE(int x,int y,int digging_element)15658 static boolean DigFieldByCE(int x, int y, int digging_element)
15659 {
15660   int element = Feld[x][y];
15661 
15662   if (!IS_FREE(x, y))
15663   {
15664     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15665 		  IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15666 		  ACTION_BREAKING);
15667 
15668     /* no element can dig solid indestructible elements */
15669     if (IS_INDESTRUCTIBLE(element) &&
15670 	!IS_DIGGABLE(element) &&
15671 	!IS_COLLECTIBLE(element))
15672       return FALSE;
15673 
15674     if (AmoebaNr[x][y] &&
15675 	(element == EL_AMOEBA_FULL ||
15676 	 element == EL_BD_AMOEBA ||
15677 	 element == EL_AMOEBA_GROWING))
15678     {
15679       AmoebaCnt[AmoebaNr[x][y]]--;
15680       AmoebaCnt2[AmoebaNr[x][y]]--;
15681     }
15682 
15683     if (IS_MOVING(x, y))
15684       RemoveMovingField(x, y);
15685     else
15686     {
15687       RemoveField(x, y);
15688       TEST_DrawLevelField(x, y);
15689     }
15690 
15691     /* if digged element was about to explode, prevent the explosion */
15692     ExplodeField[x][y] = EX_TYPE_NONE;
15693 
15694     PlayLevelSoundAction(x, y, action);
15695   }
15696 
15697   Store[x][y] = EL_EMPTY;
15698 
15699 #if 1
15700   /* this makes it possible to leave the removed element again */
15701   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15702     Store[x][y] = element;
15703 #else
15704   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15705   {
15706     int move_leave_element = element_info[digging_element].move_leave_element;
15707 
15708     /* this makes it possible to leave the removed element again */
15709     Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15710 		   element : move_leave_element);
15711   }
15712 #endif
15713 
15714   return TRUE;
15715 }
15716 
SnapField(struct PlayerInfo * player,int dx,int dy)15717 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15718 {
15719   int jx = player->jx, jy = player->jy;
15720   int x = jx + dx, y = jy + dy;
15721   int snap_direction = (dx == -1 ? MV_LEFT  :
15722 			dx == +1 ? MV_RIGHT :
15723 			dy == -1 ? MV_UP    :
15724 			dy == +1 ? MV_DOWN  : MV_NONE);
15725   boolean can_continue_snapping = (level.continuous_snapping &&
15726 				   WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15727 
15728   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15729     return FALSE;
15730 
15731   if (!player->active || !IN_LEV_FIELD(x, y))
15732     return FALSE;
15733 
15734   if (dx && dy)
15735     return FALSE;
15736 
15737   if (!dx && !dy)
15738   {
15739     if (player->MovPos == 0)
15740       player->is_pushing = FALSE;
15741 
15742     player->is_snapping = FALSE;
15743 
15744     if (player->MovPos == 0)
15745     {
15746       player->is_moving = FALSE;
15747       player->is_digging = FALSE;
15748       player->is_collecting = FALSE;
15749     }
15750 
15751     return FALSE;
15752   }
15753 
15754 #if USE_NEW_CONTINUOUS_SNAPPING
15755   /* prevent snapping with already pressed snap key when not allowed */
15756   if (player->is_snapping && !can_continue_snapping)
15757     return FALSE;
15758 #else
15759   if (player->is_snapping)
15760     return FALSE;
15761 #endif
15762 
15763   player->MovDir = snap_direction;
15764 
15765   if (player->MovPos == 0)
15766   {
15767     player->is_moving = FALSE;
15768     player->is_digging = FALSE;
15769     player->is_collecting = FALSE;
15770   }
15771 
15772   player->is_dropping = FALSE;
15773   player->is_dropping_pressed = FALSE;
15774   player->drop_pressed_delay = 0;
15775 
15776   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15777     return FALSE;
15778 
15779   player->is_snapping = TRUE;
15780   player->is_active = TRUE;
15781 
15782   if (player->MovPos == 0)
15783   {
15784     player->is_moving = FALSE;
15785     player->is_digging = FALSE;
15786     player->is_collecting = FALSE;
15787   }
15788 
15789   if (player->MovPos != 0)	/* prevent graphic bugs in versions < 2.2.0 */
15790     TEST_DrawLevelField(player->last_jx, player->last_jy);
15791 
15792   TEST_DrawLevelField(x, y);
15793 
15794   return TRUE;
15795 }
15796 
DropElement(struct PlayerInfo * player)15797 static boolean DropElement(struct PlayerInfo *player)
15798 {
15799   int old_element, new_element;
15800   int dropx = player->jx, dropy = player->jy;
15801   int drop_direction = player->MovDir;
15802   int drop_side = drop_direction;
15803 #if 1
15804   int drop_element = get_next_dropped_element(player);
15805 #else
15806   int drop_element = (player->inventory_size > 0 ?
15807 		      player->inventory_element[player->inventory_size - 1] :
15808 		      player->inventory_infinite_element != EL_UNDEFINED ?
15809 		      player->inventory_infinite_element :
15810 		      player->dynabombs_left > 0 ?
15811 		      EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15812 		      EL_UNDEFINED);
15813 #endif
15814 
15815   player->is_dropping_pressed = TRUE;
15816 
15817   /* do not drop an element on top of another element; when holding drop key
15818      pressed without moving, dropped element must move away before the next
15819      element can be dropped (this is especially important if the next element
15820      is dynamite, which can be placed on background for historical reasons) */
15821   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15822     return MP_ACTION;
15823 
15824   if (IS_THROWABLE(drop_element))
15825   {
15826     dropx += GET_DX_FROM_DIR(drop_direction);
15827     dropy += GET_DY_FROM_DIR(drop_direction);
15828 
15829     if (!IN_LEV_FIELD(dropx, dropy))
15830       return FALSE;
15831   }
15832 
15833   old_element = Feld[dropx][dropy];	/* old element at dropping position */
15834   new_element = drop_element;		/* default: no change when dropping */
15835 
15836   /* check if player is active, not moving and ready to drop */
15837   if (!player->active || player->MovPos || player->drop_delay > 0)
15838     return FALSE;
15839 
15840   /* check if player has anything that can be dropped */
15841   if (new_element == EL_UNDEFINED)
15842     return FALSE;
15843 
15844   /* check if drop key was pressed long enough for EM style dynamite */
15845   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15846     return FALSE;
15847 
15848   /* check if anything can be dropped at the current position */
15849   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15850     return FALSE;
15851 
15852   /* collected custom elements can only be dropped on empty fields */
15853   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15854     return FALSE;
15855 
15856   if (old_element != EL_EMPTY)
15857     Back[dropx][dropy] = old_element;	/* store old element on this field */
15858 
15859   ResetGfxAnimation(dropx, dropy);
15860   ResetRandomAnimationValue(dropx, dropy);
15861 
15862   if (player->inventory_size > 0 ||
15863       player->inventory_infinite_element != EL_UNDEFINED)
15864   {
15865     if (player->inventory_size > 0)
15866     {
15867       player->inventory_size--;
15868 
15869       DrawGameDoorValues();
15870 
15871       if (new_element == EL_DYNAMITE)
15872 	new_element = EL_DYNAMITE_ACTIVE;
15873       else if (new_element == EL_EM_DYNAMITE)
15874 	new_element = EL_EM_DYNAMITE_ACTIVE;
15875       else if (new_element == EL_SP_DISK_RED)
15876 	new_element = EL_SP_DISK_RED_ACTIVE;
15877     }
15878 
15879     Feld[dropx][dropy] = new_element;
15880 
15881     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15882       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15883 			  el2img(Feld[dropx][dropy]), 0);
15884 
15885     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15886 
15887     /* needed if previous element just changed to "empty" in the last frame */
15888     ChangeCount[dropx][dropy] = 0;	/* allow at least one more change */
15889 
15890     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15891 			       player->index_bit, drop_side);
15892     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15893 					CE_PLAYER_DROPS_X,
15894 					player->index_bit, drop_side);
15895 
15896     TestIfElementTouchesCustomElement(dropx, dropy);
15897   }
15898   else		/* player is dropping a dyna bomb */
15899   {
15900     player->dynabombs_left--;
15901 
15902     Feld[dropx][dropy] = new_element;
15903 
15904     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15905       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15906 			  el2img(Feld[dropx][dropy]), 0);
15907 
15908     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15909   }
15910 
15911   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15912     InitField_WithBug1(dropx, dropy, FALSE);
15913 
15914   new_element = Feld[dropx][dropy];	/* element might have changed */
15915 
15916   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15917       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15918   {
15919     int move_direction, nextx, nexty;
15920 
15921     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15922       MovDir[dropx][dropy] = drop_direction;
15923 
15924     move_direction = MovDir[dropx][dropy];
15925     nextx = dropx + GET_DX_FROM_DIR(move_direction);
15926     nexty = dropy + GET_DY_FROM_DIR(move_direction);
15927 
15928     ChangeCount[dropx][dropy] = 0;	/* allow at least one more change */
15929 
15930 #if USE_FIX_IMPACT_COLLISION
15931     /* do not cause impact style collision by dropping elements that can fall */
15932     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15933 #else
15934     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15935 #endif
15936   }
15937 
15938   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15939   player->is_dropping = TRUE;
15940 
15941   player->drop_pressed_delay = 0;
15942   player->is_dropping_pressed = FALSE;
15943 
15944   player->drop_x = dropx;
15945   player->drop_y = dropy;
15946 
15947   return TRUE;
15948 }
15949 
15950 /* ------------------------------------------------------------------------- */
15951 /* game sound playing functions                                              */
15952 /* ------------------------------------------------------------------------- */
15953 
15954 static int *loop_sound_frame = NULL;
15955 static int *loop_sound_volume = NULL;
15956 
InitPlayLevelSound()15957 void InitPlayLevelSound()
15958 {
15959   int num_sounds = getSoundListSize();
15960 
15961   checked_free(loop_sound_frame);
15962   checked_free(loop_sound_volume);
15963 
15964   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15965   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15966 }
15967 
PlayLevelSound(int x,int y,int nr)15968 static void PlayLevelSound(int x, int y, int nr)
15969 {
15970   int sx = SCREENX(x), sy = SCREENY(y);
15971   int volume, stereo_position;
15972   int max_distance = 8;
15973   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15974 
15975   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15976       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15977     return;
15978 
15979   if (!IN_LEV_FIELD(x, y) ||
15980       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15981       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15982     return;
15983 
15984   volume = SOUND_MAX_VOLUME;
15985 
15986   if (!IN_SCR_FIELD(sx, sy))
15987   {
15988     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15989     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15990 
15991     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15992   }
15993 
15994   stereo_position = (SOUND_MAX_LEFT +
15995 		     (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15996 		     (SCR_FIELDX + 2 * max_distance));
15997 
15998   if (IS_LOOP_SOUND(nr))
15999   {
16000     /* This assures that quieter loop sounds do not overwrite louder ones,
16001        while restarting sound volume comparison with each new game frame. */
16002 
16003     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
16004       return;
16005 
16006     loop_sound_volume[nr] = volume;
16007     loop_sound_frame[nr] = FrameCounter;
16008   }
16009 
16010   PlaySoundExt(nr, volume, stereo_position, type);
16011 }
16012 
PlayLevelSoundNearest(int x,int y,int sound_action)16013 static void PlayLevelSoundNearest(int x, int y, int sound_action)
16014 {
16015   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
16016 		 x > LEVELX(BX2) ? LEVELX(BX2) : x,
16017 		 y < LEVELY(BY1) ? LEVELY(BY1) :
16018 		 y > LEVELY(BY2) ? LEVELY(BY2) : y,
16019 		 sound_action);
16020 }
16021 
PlayLevelSoundAction(int x,int y,int action)16022 static void PlayLevelSoundAction(int x, int y, int action)
16023 {
16024   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
16025 }
16026 
PlayLevelSoundElementAction(int x,int y,int element,int action)16027 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
16028 {
16029   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16030 
16031   if (sound_effect != SND_UNDEFINED)
16032     PlayLevelSound(x, y, sound_effect);
16033 }
16034 
PlayLevelSoundElementActionIfLoop(int x,int y,int element,int action)16035 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
16036 					      int action)
16037 {
16038   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
16039 
16040   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16041     PlayLevelSound(x, y, sound_effect);
16042 }
16043 
PlayLevelSoundActionIfLoop(int x,int y,int action)16044 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
16045 {
16046   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16047 
16048   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16049     PlayLevelSound(x, y, sound_effect);
16050 }
16051 
StopLevelSoundActionIfLoop(int x,int y,int action)16052 static void StopLevelSoundActionIfLoop(int x, int y, int action)
16053 {
16054   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
16055 
16056   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
16057     StopSound(sound_effect);
16058 }
16059 
PlayLevelMusic()16060 static void PlayLevelMusic()
16061 {
16062   if (levelset.music[level_nr] != MUS_UNDEFINED)
16063     PlayMusic(levelset.music[level_nr]);	/* from config file */
16064   else
16065     PlayMusic(MAP_NOCONF_MUSIC(level_nr));	/* from music dir */
16066 }
16067 
PlayLevelSound_EM(int xx,int yy,int element_em,int sample)16068 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
16069 {
16070   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
16071   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
16072   int x = xx - 1 - offset;
16073   int y = yy - 1 - offset;
16074 
16075   switch (sample)
16076   {
16077     case SAMPLE_blank:
16078       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
16079       break;
16080 
16081     case SAMPLE_roll:
16082       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16083       break;
16084 
16085     case SAMPLE_stone:
16086       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16087       break;
16088 
16089     case SAMPLE_nut:
16090       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16091       break;
16092 
16093     case SAMPLE_crack:
16094       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16095       break;
16096 
16097     case SAMPLE_bug:
16098       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16099       break;
16100 
16101     case SAMPLE_tank:
16102       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16103       break;
16104 
16105     case SAMPLE_android_clone:
16106       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16107       break;
16108 
16109     case SAMPLE_android_move:
16110       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16111       break;
16112 
16113     case SAMPLE_spring:
16114       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16115       break;
16116 
16117     case SAMPLE_slurp:
16118       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16119       break;
16120 
16121     case SAMPLE_eater:
16122       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16123       break;
16124 
16125     case SAMPLE_eater_eat:
16126       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16127       break;
16128 
16129     case SAMPLE_alien:
16130       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16131       break;
16132 
16133     case SAMPLE_collect:
16134       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16135       break;
16136 
16137     case SAMPLE_diamond:
16138       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16139       break;
16140 
16141     case SAMPLE_squash:
16142       /* !!! CHECK THIS !!! */
16143 #if 1
16144       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16145 #else
16146       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16147 #endif
16148       break;
16149 
16150     case SAMPLE_wonderfall:
16151       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16152       break;
16153 
16154     case SAMPLE_drip:
16155       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16156       break;
16157 
16158     case SAMPLE_push:
16159       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16160       break;
16161 
16162     case SAMPLE_dirt:
16163       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16164       break;
16165 
16166     case SAMPLE_acid:
16167       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16168       break;
16169 
16170     case SAMPLE_ball:
16171       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16172       break;
16173 
16174     case SAMPLE_grow:
16175       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16176       break;
16177 
16178     case SAMPLE_wonder:
16179       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16180       break;
16181 
16182     case SAMPLE_door:
16183       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16184       break;
16185 
16186     case SAMPLE_exit_open:
16187       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16188       break;
16189 
16190     case SAMPLE_exit_leave:
16191       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16192       break;
16193 
16194     case SAMPLE_dynamite:
16195       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16196       break;
16197 
16198     case SAMPLE_tick:
16199       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16200       break;
16201 
16202     case SAMPLE_press:
16203       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16204       break;
16205 
16206     case SAMPLE_wheel:
16207       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16208       break;
16209 
16210     case SAMPLE_boom:
16211       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16212       break;
16213 
16214     case SAMPLE_die:
16215       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16216       break;
16217 
16218     case SAMPLE_time:
16219       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16220       break;
16221 
16222     default:
16223       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16224       break;
16225   }
16226 }
16227 
PlayLevelSound_SP(int xx,int yy,int element_sp,int action_sp)16228 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16229 {
16230   int element = map_element_SP_to_RND(element_sp);
16231   int action = map_action_SP_to_RND(action_sp);
16232   int offset = (setup.sp_show_border_elements ? 0 : 1);
16233   int x = xx - offset;
16234   int y = yy - offset;
16235 
16236 #if 0
16237   printf("::: %d -> %d\n", element_sp, action_sp);
16238 #endif
16239 
16240   PlayLevelSoundElementAction(x, y, element, action);
16241 }
16242 
16243 #if 0
16244 void ChangeTime(int value)
16245 {
16246   int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16247 
16248   *time += value;
16249 
16250   /* EMC game engine uses value from time counter of RND game engine */
16251   level.native_em_level->lev->time = *time;
16252 
16253   DrawGameValue_Time(*time);
16254 }
16255 
16256 void RaiseScore(int value)
16257 {
16258   /* EMC game engine and RND game engine have separate score counters */
16259   int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16260 		&level.native_em_level->lev->score : &local_player->score);
16261 
16262   *score += value;
16263 
16264   DrawGameValue_Score(*score);
16265 }
16266 #endif
16267 
RaiseScore(int value)16268 void RaiseScore(int value)
16269 {
16270   local_player->score += value;
16271 
16272 #if 1
16273   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16274 
16275   DisplayGameControlValues();
16276 #else
16277   DrawGameValue_Score(local_player->score);
16278 #endif
16279 }
16280 
RaiseScoreElement(int element)16281 void RaiseScoreElement(int element)
16282 {
16283   switch (element)
16284   {
16285     case EL_EMERALD:
16286     case EL_BD_DIAMOND:
16287     case EL_EMERALD_YELLOW:
16288     case EL_EMERALD_RED:
16289     case EL_EMERALD_PURPLE:
16290     case EL_SP_INFOTRON:
16291       RaiseScore(level.score[SC_EMERALD]);
16292       break;
16293     case EL_DIAMOND:
16294       RaiseScore(level.score[SC_DIAMOND]);
16295       break;
16296     case EL_CRYSTAL:
16297       RaiseScore(level.score[SC_CRYSTAL]);
16298       break;
16299     case EL_PEARL:
16300       RaiseScore(level.score[SC_PEARL]);
16301       break;
16302     case EL_BUG:
16303     case EL_BD_BUTTERFLY:
16304     case EL_SP_ELECTRON:
16305       RaiseScore(level.score[SC_BUG]);
16306       break;
16307     case EL_SPACESHIP:
16308     case EL_BD_FIREFLY:
16309     case EL_SP_SNIKSNAK:
16310       RaiseScore(level.score[SC_SPACESHIP]);
16311       break;
16312     case EL_YAMYAM:
16313     case EL_DARK_YAMYAM:
16314       RaiseScore(level.score[SC_YAMYAM]);
16315       break;
16316     case EL_ROBOT:
16317       RaiseScore(level.score[SC_ROBOT]);
16318       break;
16319     case EL_PACMAN:
16320       RaiseScore(level.score[SC_PACMAN]);
16321       break;
16322     case EL_NUT:
16323       RaiseScore(level.score[SC_NUT]);
16324       break;
16325     case EL_DYNAMITE:
16326     case EL_EM_DYNAMITE:
16327     case EL_SP_DISK_RED:
16328     case EL_DYNABOMB_INCREASE_NUMBER:
16329     case EL_DYNABOMB_INCREASE_SIZE:
16330     case EL_DYNABOMB_INCREASE_POWER:
16331       RaiseScore(level.score[SC_DYNAMITE]);
16332       break;
16333     case EL_SHIELD_NORMAL:
16334     case EL_SHIELD_DEADLY:
16335       RaiseScore(level.score[SC_SHIELD]);
16336       break;
16337     case EL_EXTRA_TIME:
16338       RaiseScore(level.extra_time_score);
16339       break;
16340     case EL_KEY_1:
16341     case EL_KEY_2:
16342     case EL_KEY_3:
16343     case EL_KEY_4:
16344     case EL_EM_KEY_1:
16345     case EL_EM_KEY_2:
16346     case EL_EM_KEY_3:
16347     case EL_EM_KEY_4:
16348     case EL_EMC_KEY_5:
16349     case EL_EMC_KEY_6:
16350     case EL_EMC_KEY_7:
16351     case EL_EMC_KEY_8:
16352     case EL_DC_KEY_WHITE:
16353       RaiseScore(level.score[SC_KEY]);
16354       break;
16355     default:
16356       RaiseScore(element_info[element].collect_score);
16357       break;
16358   }
16359 }
16360 
RequestQuitGameExt(boolean skip_request,boolean quick_quit,char * message)16361 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16362 {
16363   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16364   {
16365 #if defined(NETWORK_AVALIABLE)
16366     if (options.network)
16367       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16368     else
16369 #endif
16370     {
16371       if (quick_quit)
16372       {
16373 #if 1
16374 
16375 #if 1
16376 	FadeSkipNextFadeIn();
16377 #else
16378 	fading = fading_none;
16379 #endif
16380 
16381 #else
16382 	OpenDoor(DOOR_CLOSE_1);
16383 #endif
16384 
16385 	game_status = GAME_MODE_MAIN;
16386 
16387 #if 1
16388 	DrawAndFadeInMainMenu(REDRAW_FIELD);
16389 #else
16390 	DrawMainMenu();
16391 #endif
16392       }
16393       else
16394       {
16395 #if 0
16396 	FadeOut(REDRAW_FIELD);
16397 #endif
16398 
16399 	game_status = GAME_MODE_MAIN;
16400 
16401 	DrawAndFadeInMainMenu(REDRAW_FIELD);
16402       }
16403     }
16404   }
16405   else		/* continue playing the game */
16406   {
16407     if (tape.playing && tape.deactivate_display)
16408       TapeDeactivateDisplayOff(TRUE);
16409 
16410     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16411 
16412     if (tape.playing && tape.deactivate_display)
16413       TapeDeactivateDisplayOn();
16414   }
16415 }
16416 
RequestQuitGame(boolean ask_if_really_quit)16417 void RequestQuitGame(boolean ask_if_really_quit)
16418 {
16419   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16420   boolean skip_request = AllPlayersGone || quick_quit;
16421 
16422   RequestQuitGameExt(skip_request, quick_quit,
16423 		     "Do you really want to quit the game ?");
16424 }
16425 
16426 
16427 /* ------------------------------------------------------------------------- */
16428 /* random generator functions                                                */
16429 /* ------------------------------------------------------------------------- */
16430 
InitEngineRandom_RND(long seed)16431 unsigned int InitEngineRandom_RND(long seed)
16432 {
16433   game.num_random_calls = 0;
16434 
16435 #if 0
16436   unsigned int rnd_seed = InitEngineRandom(seed);
16437 
16438   printf("::: START RND: %d\n", rnd_seed);
16439 
16440   return rnd_seed;
16441 #else
16442 
16443   return InitEngineRandom(seed);
16444 
16445 #endif
16446 
16447 }
16448 
RND(int max)16449 unsigned int RND(int max)
16450 {
16451   if (max > 0)
16452   {
16453     game.num_random_calls++;
16454 
16455     return GetEngineRandom(max);
16456   }
16457 
16458   return 0;
16459 }
16460 
16461 
16462 /* ------------------------------------------------------------------------- */
16463 /* game engine snapshot handling functions                                   */
16464 /* ------------------------------------------------------------------------- */
16465 
16466 struct EngineSnapshotInfo
16467 {
16468   /* runtime values for custom element collect score */
16469   int collect_score[NUM_CUSTOM_ELEMENTS];
16470 
16471   /* runtime values for group element choice position */
16472   int choice_pos[NUM_GROUP_ELEMENTS];
16473 
16474   /* runtime values for belt position animations */
16475   int belt_graphic[4][NUM_BELT_PARTS];
16476   int belt_anim_mode[4][NUM_BELT_PARTS];
16477 };
16478 
16479 static struct EngineSnapshotInfo engine_snapshot_rnd;
16480 static char *snapshot_level_identifier = NULL;
16481 static int snapshot_level_nr = -1;
16482 
SaveEngineSnapshotValues_RND()16483 static void SaveEngineSnapshotValues_RND()
16484 {
16485   static int belt_base_active_element[4] =
16486   {
16487     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16488     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16489     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16490     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16491   };
16492   int i, j;
16493 
16494   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16495   {
16496     int element = EL_CUSTOM_START + i;
16497 
16498     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16499   }
16500 
16501   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16502   {
16503     int element = EL_GROUP_START + i;
16504 
16505     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16506   }
16507 
16508   for (i = 0; i < 4; i++)
16509   {
16510     for (j = 0; j < NUM_BELT_PARTS; j++)
16511     {
16512       int element = belt_base_active_element[i] + j;
16513       int graphic = el2img(element);
16514       int anim_mode = graphic_info[graphic].anim_mode;
16515 
16516       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16517       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16518     }
16519   }
16520 }
16521 
LoadEngineSnapshotValues_RND()16522 static void LoadEngineSnapshotValues_RND()
16523 {
16524   unsigned long num_random_calls = game.num_random_calls;
16525   int i, j;
16526 
16527   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16528   {
16529     int element = EL_CUSTOM_START + i;
16530 
16531     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16532   }
16533 
16534   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16535   {
16536     int element = EL_GROUP_START + i;
16537 
16538     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16539   }
16540 
16541   for (i = 0; i < 4; i++)
16542   {
16543     for (j = 0; j < NUM_BELT_PARTS; j++)
16544     {
16545       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16546       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16547 
16548       graphic_info[graphic].anim_mode = anim_mode;
16549     }
16550   }
16551 
16552   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16553   {
16554     InitRND(tape.random_seed);
16555     for (i = 0; i < num_random_calls; i++)
16556       RND(1);
16557   }
16558 
16559   if (game.num_random_calls != num_random_calls)
16560   {
16561     Error(ERR_INFO, "number of random calls out of sync");
16562     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16563     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16564     Error(ERR_EXIT, "this should not happen -- please debug");
16565   }
16566 }
16567 
SaveEngineSnapshot()16568 void SaveEngineSnapshot()
16569 {
16570   /* do not save snapshots from editor */
16571   if (level_editor_test_game)
16572     return;
16573 
16574   /* free previous snapshot buffers, if needed */
16575   FreeEngineSnapshotBuffers();
16576 
16577   /* copy some special values to a structure better suited for the snapshot */
16578 
16579   SaveEngineSnapshotValues_RND();
16580   SaveEngineSnapshotValues_EM();
16581   SaveEngineSnapshotValues_SP();
16582 
16583   /* save values stored in special snapshot structure */
16584 
16585   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16586   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16587   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16588 
16589   /* save further RND engine values */
16590 
16591   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16592   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16593   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16594 
16595   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16596   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16597   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16598   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16599 
16600   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16601   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16602   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16603   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16604   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16605 
16606   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16607   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16608   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16609 
16610   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16611 
16612   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16613 
16614   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16615   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16616 
16617   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16618   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16619   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16620   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16621   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16622   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16623   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16624   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16625   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16626   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16627   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16628   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16629   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16630   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16631   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16632   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16633   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16634   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16635 
16636   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16637   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16638 
16639   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16640   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16641   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16642 
16643   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16644   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16645 
16646   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16647   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16648   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16649   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16650   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16651 
16652   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16653   SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16654 
16655   /* save level identification information */
16656 
16657   setString(&snapshot_level_identifier, leveldir_current->identifier);
16658   snapshot_level_nr = level_nr;
16659 
16660 #if 0
16661   ListNode *node = engine_snapshot_list_rnd;
16662   int num_bytes = 0;
16663 
16664   while (node != NULL)
16665   {
16666     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16667 
16668     node = node->next;
16669   }
16670 
16671   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16672 #endif
16673 }
16674 
LoadEngineSnapshot()16675 void LoadEngineSnapshot()
16676 {
16677   /* restore generically stored snapshot buffers */
16678 
16679   LoadEngineSnapshotBuffers();
16680 
16681   /* restore special values from snapshot structure */
16682 
16683   LoadEngineSnapshotValues_RND();
16684   LoadEngineSnapshotValues_EM();
16685   LoadEngineSnapshotValues_SP();
16686 }
16687 
CheckEngineSnapshot()16688 boolean CheckEngineSnapshot()
16689 {
16690   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16691 	  snapshot_level_nr == level_nr);
16692 }
16693 
16694 
16695 /* ---------- new game button stuff ---------------------------------------- */
16696 
16697 /* graphic position values for game buttons */
16698 #define GAME_BUTTON_XSIZE	30
16699 #define GAME_BUTTON_YSIZE	30
16700 #define GAME_BUTTON_XPOS	5
16701 #define GAME_BUTTON_YPOS	215
16702 #define SOUND_BUTTON_XPOS	5
16703 #define SOUND_BUTTON_YPOS	(GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16704 
16705 #define GAME_BUTTON_STOP_XPOS	(GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16706 #define GAME_BUTTON_PAUSE_XPOS	(GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16707 #define GAME_BUTTON_PLAY_XPOS	(GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16708 #define SOUND_BUTTON_MUSIC_XPOS	(SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16709 #define SOUND_BUTTON_LOOPS_XPOS	(SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16710 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16711 
16712 static struct
16713 {
16714   int *x, *y;
16715   int gd_x, gd_y;
16716   int gadget_id;
16717   char *infotext;
16718 } gamebutton_info[NUM_GAME_BUTTONS] =
16719 {
16720 #if 1
16721   {
16722     &game.button.stop.x,	&game.button.stop.y,
16723     GAME_BUTTON_STOP_XPOS,	GAME_BUTTON_YPOS,
16724     GAME_CTRL_ID_STOP,
16725     "stop game"
16726   },
16727   {
16728     &game.button.pause.x,	&game.button.pause.y,
16729     GAME_BUTTON_PAUSE_XPOS,	GAME_BUTTON_YPOS,
16730     GAME_CTRL_ID_PAUSE,
16731     "pause game"
16732   },
16733   {
16734     &game.button.play.x,	&game.button.play.y,
16735     GAME_BUTTON_PLAY_XPOS,	GAME_BUTTON_YPOS,
16736     GAME_CTRL_ID_PLAY,
16737     "play game"
16738   },
16739   {
16740     &game.button.sound_music.x,	&game.button.sound_music.y,
16741     SOUND_BUTTON_MUSIC_XPOS,	SOUND_BUTTON_YPOS,
16742     SOUND_CTRL_ID_MUSIC,
16743     "background music on/off"
16744   },
16745   {
16746     &game.button.sound_loops.x,	&game.button.sound_loops.y,
16747     SOUND_BUTTON_LOOPS_XPOS,	SOUND_BUTTON_YPOS,
16748     SOUND_CTRL_ID_LOOPS,
16749     "sound loops on/off"
16750   },
16751   {
16752     &game.button.sound_simple.x,&game.button.sound_simple.y,
16753     SOUND_BUTTON_SIMPLE_XPOS,	SOUND_BUTTON_YPOS,
16754     SOUND_CTRL_ID_SIMPLE,
16755     "normal sounds on/off"
16756   }
16757 #else
16758   {
16759     GAME_BUTTON_STOP_XPOS,	GAME_BUTTON_YPOS,
16760     GAME_CTRL_ID_STOP,
16761     "stop game"
16762   },
16763   {
16764     GAME_BUTTON_PAUSE_XPOS,	GAME_BUTTON_YPOS,
16765     GAME_CTRL_ID_PAUSE,
16766     "pause game"
16767   },
16768   {
16769     GAME_BUTTON_PLAY_XPOS,	GAME_BUTTON_YPOS,
16770     GAME_CTRL_ID_PLAY,
16771     "play game"
16772   },
16773   {
16774     SOUND_BUTTON_MUSIC_XPOS,	SOUND_BUTTON_YPOS,
16775     SOUND_CTRL_ID_MUSIC,
16776     "background music on/off"
16777   },
16778   {
16779     SOUND_BUTTON_LOOPS_XPOS,	SOUND_BUTTON_YPOS,
16780     SOUND_CTRL_ID_LOOPS,
16781     "sound loops on/off"
16782   },
16783   {
16784     SOUND_BUTTON_SIMPLE_XPOS,	SOUND_BUTTON_YPOS,
16785     SOUND_CTRL_ID_SIMPLE,
16786     "normal sounds on/off"
16787   }
16788 #endif
16789 };
16790 
CreateGameButtons()16791 void CreateGameButtons()
16792 {
16793   int i;
16794 
16795   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16796   {
16797     Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16798     struct GadgetInfo *gi;
16799     int button_type;
16800     boolean checked;
16801     unsigned long event_mask;
16802     int x, y;
16803     int gd_xoffset, gd_yoffset;
16804     int gd_x1, gd_x2, gd_y1, gd_y2;
16805     int id = i;
16806 
16807     x = DX + *gamebutton_info[i].x;
16808     y = DY + *gamebutton_info[i].y;
16809     gd_xoffset = gamebutton_info[i].gd_x;
16810     gd_yoffset = gamebutton_info[i].gd_y;
16811     gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16812     gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16813 
16814     if (id == GAME_CTRL_ID_STOP ||
16815 	id == GAME_CTRL_ID_PAUSE ||
16816 	id == GAME_CTRL_ID_PLAY)
16817     {
16818       button_type = GD_TYPE_NORMAL_BUTTON;
16819       checked = FALSE;
16820       event_mask = GD_EVENT_RELEASED;
16821       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16822       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16823     }
16824     else
16825     {
16826       button_type = GD_TYPE_CHECK_BUTTON;
16827       checked =
16828 	((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16829 	 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16830 	 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16831       event_mask = GD_EVENT_PRESSED;
16832       gd_y1  = DOOR_GFX_PAGEY1 + gd_yoffset;
16833       gd_y2  = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16834     }
16835 
16836     gi = CreateGadget(GDI_CUSTOM_ID, id,
16837 		      GDI_INFO_TEXT, gamebutton_info[i].infotext,
16838 #if 1
16839 		      GDI_X, x,
16840 		      GDI_Y, y,
16841 #else
16842 		      GDI_X, DX + gd_xoffset,
16843 		      GDI_Y, DY + gd_yoffset,
16844 #endif
16845 		      GDI_WIDTH, GAME_BUTTON_XSIZE,
16846 		      GDI_HEIGHT, GAME_BUTTON_YSIZE,
16847 		      GDI_TYPE, button_type,
16848 		      GDI_STATE, GD_BUTTON_UNPRESSED,
16849 		      GDI_CHECKED, checked,
16850 		      GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16851 		      GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16852 		      GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16853 		      GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16854 		      GDI_DIRECT_DRAW, FALSE,
16855 		      GDI_EVENT_MASK, event_mask,
16856 		      GDI_CALLBACK_ACTION, HandleGameButtons,
16857 		      GDI_END);
16858 
16859     if (gi == NULL)
16860       Error(ERR_EXIT, "cannot create gadget");
16861 
16862     game_gadget[id] = gi;
16863   }
16864 }
16865 
FreeGameButtons()16866 void FreeGameButtons()
16867 {
16868   int i;
16869 
16870   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16871     FreeGadget(game_gadget[i]);
16872 }
16873 
MapGameButtons()16874 static void MapGameButtons()
16875 {
16876   int i;
16877 
16878   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16879     MapGadget(game_gadget[i]);
16880 }
16881 
UnmapGameButtons()16882 void UnmapGameButtons()
16883 {
16884   int i;
16885 
16886   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16887     UnmapGadget(game_gadget[i]);
16888 }
16889 
RedrawGameButtons()16890 void RedrawGameButtons()
16891 {
16892   int i;
16893 
16894   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16895     RedrawGadget(game_gadget[i]);
16896 }
16897 
HandleGameButtonsExt(int id)16898 static void HandleGameButtonsExt(int id)
16899 {
16900   if (game_status != GAME_MODE_PLAYING)
16901     return;
16902 
16903   switch (id)
16904   {
16905     case GAME_CTRL_ID_STOP:
16906       if (tape.playing)
16907 	TapeStop();
16908       else
16909 	RequestQuitGame(TRUE);
16910       break;
16911 
16912     case GAME_CTRL_ID_PAUSE:
16913       if (options.network)
16914       {
16915 #if defined(NETWORK_AVALIABLE)
16916 	if (tape.pausing)
16917 	  SendToServer_ContinuePlaying();
16918 	else
16919 	  SendToServer_PausePlaying();
16920 #endif
16921       }
16922       else
16923 	TapeTogglePause(TAPE_TOGGLE_MANUAL);
16924       break;
16925 
16926     case GAME_CTRL_ID_PLAY:
16927       if (tape.pausing)
16928       {
16929 #if defined(NETWORK_AVALIABLE)
16930 	if (options.network)
16931 	  SendToServer_ContinuePlaying();
16932 	else
16933 #endif
16934 	{
16935 	  tape.pausing = FALSE;
16936 	  DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16937 	}
16938       }
16939       break;
16940 
16941     case SOUND_CTRL_ID_MUSIC:
16942       if (setup.sound_music)
16943       {
16944 	setup.sound_music = FALSE;
16945 
16946 	FadeMusic();
16947       }
16948       else if (audio.music_available)
16949       {
16950 	setup.sound = setup.sound_music = TRUE;
16951 
16952 	SetAudioMode(setup.sound);
16953 
16954 	PlayLevelMusic();
16955       }
16956       break;
16957 
16958     case SOUND_CTRL_ID_LOOPS:
16959       if (setup.sound_loops)
16960 	setup.sound_loops = FALSE;
16961       else if (audio.loops_available)
16962       {
16963 	setup.sound = setup.sound_loops = TRUE;
16964 
16965 	SetAudioMode(setup.sound);
16966       }
16967       break;
16968 
16969     case SOUND_CTRL_ID_SIMPLE:
16970       if (setup.sound_simple)
16971 	setup.sound_simple = FALSE;
16972       else if (audio.sound_available)
16973       {
16974 	setup.sound = setup.sound_simple = TRUE;
16975 
16976 	SetAudioMode(setup.sound);
16977       }
16978       break;
16979 
16980     default:
16981       break;
16982   }
16983 }
16984 
HandleGameButtons(struct GadgetInfo * gi)16985 static void HandleGameButtons(struct GadgetInfo *gi)
16986 {
16987   HandleGameButtonsExt(gi->custom_id);
16988 }
16989 
HandleSoundButtonKeys(Key key)16990 void HandleSoundButtonKeys(Key key)
16991 {
16992 #if 1
16993   if (key == setup.shortcut.sound_simple)
16994     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16995   else if (key == setup.shortcut.sound_loops)
16996     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16997   else if (key == setup.shortcut.sound_music)
16998     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16999 #else
17000   if (key == setup.shortcut.sound_simple)
17001     HandleGameButtonsExt(SOUND_CTRL_ID_SIMPLE);
17002   else if (key == setup.shortcut.sound_loops)
17003     HandleGameButtonsExt(SOUND_CTRL_ID_LOOPS);
17004   else if (key == setup.shortcut.sound_music)
17005     HandleGameButtonsExt(SOUND_CTRL_ID_MUSIC);
17006 #endif
17007 }
17008