1 /***********************************************************
2 * Rocks'n'Diamonds -- McDuffin Strikes Back! *
3 *----------------------------------------------------------*
4 * (c) 1995-2006 Artsoft Entertainment *
6 * Detmolder Strasse 189 *
9 * e-mail: info@artsoft.org *
10 *----------------------------------------------------------*
12 ***********************************************************/
14 #include "libgame/libgame.h"
24 /* EXPERIMENTAL STUFF */
25 #define USE_NEW_AMOEBA_CODE FALSE
27 /* EXPERIMENTAL STUFF */
28 #define USE_NEW_STUFF ( 1)
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)
50 #define USE_QUICKSAND_IMPACT_BUGFIX (USE_NEW_STUFF * 0)
52 #define USE_CODE_THAT_BREAKS_SNAKE_BITE (USE_NEW_STUFF * 1)
54 #define USE_UFAST_PLAYER_EXIT_BUGFIX (USE_NEW_STUFF * 1)
56 #define USE_GFX_RESET_ONLY_WHEN_MOVING (USE_NEW_STUFF * 1)
57 #define USE_GFX_RESET_PLAYER_ARTWORK (USE_NEW_STUFF * 1)
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)
64 #define USE_PLAYER_REANIMATION (USE_NEW_STUFF * 1)
66 #define USE_GFX_RESET_WHEN_NOT_MOVING (USE_NEW_STUFF * 1)
68 #define USE_NEW_PLAYER_ASSIGNMENTS (USE_NEW_STUFF * 1)
70 #define USE_DELAYED_GFX_REDRAW (USE_NEW_STUFF * 0)
72 #if USE_DELAYED_GFX_REDRAW
73 #define TEST_DrawLevelField(x, y) \
74 GfxRedraw[x][y] |= GFX_REDRAW_TILE
75 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
76 GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
77 #define TEST_DrawLevelFieldCrumbledSandNeighbours(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
82 #define TEST_DrawLevelField(x, y) \
84 #define TEST_DrawLevelFieldCrumbledSand(x, y) \
85 DrawLevelFieldCrumbledSand(x, y)
86 #define TEST_DrawLevelFieldCrumbledSandNeighbours(x, y) \
87 DrawLevelFieldCrumbledSandNeighbours(x, y)
88 #define TEST_DrawTwinkleOnField(x, y) \
89 DrawTwinkleOnField(x, y)
98 /* for MovePlayer() */
99 #define MP_NO_ACTION 0
102 #define MP_DONT_RUN_INTO (MP_MOVING | MP_ACTION)
104 /* for ScrollPlayer() */
105 #define SCROLL_INIT 0
106 #define SCROLL_GO_ON 1
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)
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))
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))
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)
160 /* game panel display and control definitions */
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_SHIELD_NORMAL 35
198 #define GAME_PANEL_SHIELD_NORMAL_TIME 36
199 #define GAME_PANEL_SHIELD_DEADLY 37
200 #define GAME_PANEL_SHIELD_DEADLY_TIME 38
201 #define GAME_PANEL_EXIT 39
202 #define GAME_PANEL_EMC_MAGIC_BALL 40
203 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH 41
204 #define GAME_PANEL_LIGHT_SWITCH 42
205 #define GAME_PANEL_LIGHT_SWITCH_TIME 43
206 #define GAME_PANEL_TIMEGATE_SWITCH 44
207 #define GAME_PANEL_TIMEGATE_SWITCH_TIME 45
208 #define GAME_PANEL_SWITCHGATE_SWITCH 46
209 #define GAME_PANEL_EMC_LENSES 47
210 #define GAME_PANEL_EMC_LENSES_TIME 48
211 #define GAME_PANEL_EMC_MAGNIFIER 49
212 #define GAME_PANEL_EMC_MAGNIFIER_TIME 50
213 #define GAME_PANEL_BALLOON_SWITCH 51
214 #define GAME_PANEL_DYNABOMB_NUMBER 52
215 #define GAME_PANEL_DYNABOMB_SIZE 53
216 #define GAME_PANEL_DYNABOMB_POWER 54
217 #define GAME_PANEL_PENGUINS 55
218 #define GAME_PANEL_SOKOBAN_OBJECTS 56
219 #define GAME_PANEL_SOKOBAN_FIELDS 57
220 #define GAME_PANEL_ROBOT_WHEEL 58
221 #define GAME_PANEL_CONVEYOR_BELT_1 59
222 #define GAME_PANEL_CONVEYOR_BELT_2 60
223 #define GAME_PANEL_CONVEYOR_BELT_3 61
224 #define GAME_PANEL_CONVEYOR_BELT_4 62
225 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH 63
226 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH 64
227 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH 65
228 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH 66
229 #define GAME_PANEL_MAGIC_WALL 67
230 #define GAME_PANEL_MAGIC_WALL_TIME 68
231 #define GAME_PANEL_GRAVITY_STATE 69
232 #define GAME_PANEL_GRAPHIC_1 70
233 #define GAME_PANEL_GRAPHIC_2 71
234 #define GAME_PANEL_GRAPHIC_3 72
235 #define GAME_PANEL_GRAPHIC_4 73
236 #define GAME_PANEL_GRAPHIC_5 74
237 #define GAME_PANEL_GRAPHIC_6 75
238 #define GAME_PANEL_GRAPHIC_7 76
239 #define GAME_PANEL_GRAPHIC_8 77
240 #define GAME_PANEL_ELEMENT_1 78
241 #define GAME_PANEL_ELEMENT_2 79
242 #define GAME_PANEL_ELEMENT_3 80
243 #define GAME_PANEL_ELEMENT_4 81
244 #define GAME_PANEL_ELEMENT_5 82
245 #define GAME_PANEL_ELEMENT_6 83
246 #define GAME_PANEL_ELEMENT_7 84
247 #define GAME_PANEL_ELEMENT_8 85
248 #define GAME_PANEL_ELEMENT_COUNT_1 86
249 #define GAME_PANEL_ELEMENT_COUNT_2 87
250 #define GAME_PANEL_ELEMENT_COUNT_3 88
251 #define GAME_PANEL_ELEMENT_COUNT_4 89
252 #define GAME_PANEL_ELEMENT_COUNT_5 90
253 #define GAME_PANEL_ELEMENT_COUNT_6 91
254 #define GAME_PANEL_ELEMENT_COUNT_7 92
255 #define GAME_PANEL_ELEMENT_COUNT_8 93
256 #define GAME_PANEL_CE_SCORE_1 94
257 #define GAME_PANEL_CE_SCORE_2 95
258 #define GAME_PANEL_CE_SCORE_3 96
259 #define GAME_PANEL_CE_SCORE_4 97
260 #define GAME_PANEL_CE_SCORE_5 98
261 #define GAME_PANEL_CE_SCORE_6 99
262 #define GAME_PANEL_CE_SCORE_7 100
263 #define GAME_PANEL_CE_SCORE_8 101
264 #define GAME_PANEL_CE_SCORE_1_ELEMENT 102
265 #define GAME_PANEL_CE_SCORE_2_ELEMENT 103
266 #define GAME_PANEL_CE_SCORE_3_ELEMENT 104
267 #define GAME_PANEL_CE_SCORE_4_ELEMENT 105
268 #define GAME_PANEL_CE_SCORE_5_ELEMENT 106
269 #define GAME_PANEL_CE_SCORE_6_ELEMENT 107
270 #define GAME_PANEL_CE_SCORE_7_ELEMENT 108
271 #define GAME_PANEL_CE_SCORE_8_ELEMENT 109
272 #define GAME_PANEL_PLAYER_NAME 110
273 #define GAME_PANEL_LEVEL_NAME 111
274 #define GAME_PANEL_LEVEL_AUTHOR 112
276 #define NUM_GAME_PANEL_CONTROLS 113
278 struct GamePanelOrderInfo
284 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
286 struct GamePanelControlInfo
290 struct TextPosInfo *pos;
293 int value, last_value;
294 int frame, last_frame;
299 static struct GamePanelControlInfo game_panel_controls[] =
302 GAME_PANEL_LEVEL_NUMBER,
303 &game.panel.level_number,
312 GAME_PANEL_INVENTORY_COUNT,
313 &game.panel.inventory_count,
317 GAME_PANEL_INVENTORY_FIRST_1,
318 &game.panel.inventory_first[0],
322 GAME_PANEL_INVENTORY_FIRST_2,
323 &game.panel.inventory_first[1],
327 GAME_PANEL_INVENTORY_FIRST_3,
328 &game.panel.inventory_first[2],
332 GAME_PANEL_INVENTORY_FIRST_4,
333 &game.panel.inventory_first[3],
337 GAME_PANEL_INVENTORY_FIRST_5,
338 &game.panel.inventory_first[4],
342 GAME_PANEL_INVENTORY_FIRST_6,
343 &game.panel.inventory_first[5],
347 GAME_PANEL_INVENTORY_FIRST_7,
348 &game.panel.inventory_first[6],
352 GAME_PANEL_INVENTORY_FIRST_8,
353 &game.panel.inventory_first[7],
357 GAME_PANEL_INVENTORY_LAST_1,
358 &game.panel.inventory_last[0],
362 GAME_PANEL_INVENTORY_LAST_2,
363 &game.panel.inventory_last[1],
367 GAME_PANEL_INVENTORY_LAST_3,
368 &game.panel.inventory_last[2],
372 GAME_PANEL_INVENTORY_LAST_4,
373 &game.panel.inventory_last[3],
377 GAME_PANEL_INVENTORY_LAST_5,
378 &game.panel.inventory_last[4],
382 GAME_PANEL_INVENTORY_LAST_6,
383 &game.panel.inventory_last[5],
387 GAME_PANEL_INVENTORY_LAST_7,
388 &game.panel.inventory_last[6],
392 GAME_PANEL_INVENTORY_LAST_8,
393 &game.panel.inventory_last[7],
437 GAME_PANEL_KEY_WHITE,
438 &game.panel.key_white,
442 GAME_PANEL_KEY_WHITE_COUNT,
443 &game.panel.key_white_count,
452 GAME_PANEL_HIGHSCORE,
453 &game.panel.highscore,
477 GAME_PANEL_SHIELD_NORMAL,
478 &game.panel.shield_normal,
482 GAME_PANEL_SHIELD_NORMAL_TIME,
483 &game.panel.shield_normal_time,
487 GAME_PANEL_SHIELD_DEADLY,
488 &game.panel.shield_deadly,
492 GAME_PANEL_SHIELD_DEADLY_TIME,
493 &game.panel.shield_deadly_time,
502 GAME_PANEL_EMC_MAGIC_BALL,
503 &game.panel.emc_magic_ball,
507 GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
508 &game.panel.emc_magic_ball_switch,
512 GAME_PANEL_LIGHT_SWITCH,
513 &game.panel.light_switch,
517 GAME_PANEL_LIGHT_SWITCH_TIME,
518 &game.panel.light_switch_time,
522 GAME_PANEL_TIMEGATE_SWITCH,
523 &game.panel.timegate_switch,
527 GAME_PANEL_TIMEGATE_SWITCH_TIME,
528 &game.panel.timegate_switch_time,
532 GAME_PANEL_SWITCHGATE_SWITCH,
533 &game.panel.switchgate_switch,
537 GAME_PANEL_EMC_LENSES,
538 &game.panel.emc_lenses,
542 GAME_PANEL_EMC_LENSES_TIME,
543 &game.panel.emc_lenses_time,
547 GAME_PANEL_EMC_MAGNIFIER,
548 &game.panel.emc_magnifier,
552 GAME_PANEL_EMC_MAGNIFIER_TIME,
553 &game.panel.emc_magnifier_time,
557 GAME_PANEL_BALLOON_SWITCH,
558 &game.panel.balloon_switch,
562 GAME_PANEL_DYNABOMB_NUMBER,
563 &game.panel.dynabomb_number,
567 GAME_PANEL_DYNABOMB_SIZE,
568 &game.panel.dynabomb_size,
572 GAME_PANEL_DYNABOMB_POWER,
573 &game.panel.dynabomb_power,
578 &game.panel.penguins,
582 GAME_PANEL_SOKOBAN_OBJECTS,
583 &game.panel.sokoban_objects,
587 GAME_PANEL_SOKOBAN_FIELDS,
588 &game.panel.sokoban_fields,
592 GAME_PANEL_ROBOT_WHEEL,
593 &game.panel.robot_wheel,
597 GAME_PANEL_CONVEYOR_BELT_1,
598 &game.panel.conveyor_belt[0],
602 GAME_PANEL_CONVEYOR_BELT_2,
603 &game.panel.conveyor_belt[1],
607 GAME_PANEL_CONVEYOR_BELT_3,
608 &game.panel.conveyor_belt[2],
612 GAME_PANEL_CONVEYOR_BELT_4,
613 &game.panel.conveyor_belt[3],
617 GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
618 &game.panel.conveyor_belt_switch[0],
622 GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
623 &game.panel.conveyor_belt_switch[1],
627 GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
628 &game.panel.conveyor_belt_switch[2],
632 GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
633 &game.panel.conveyor_belt_switch[3],
637 GAME_PANEL_MAGIC_WALL,
638 &game.panel.magic_wall,
642 GAME_PANEL_MAGIC_WALL_TIME,
643 &game.panel.magic_wall_time,
647 GAME_PANEL_GRAVITY_STATE,
648 &game.panel.gravity_state,
652 GAME_PANEL_GRAPHIC_1,
653 &game.panel.graphic[0],
657 GAME_PANEL_GRAPHIC_2,
658 &game.panel.graphic[1],
662 GAME_PANEL_GRAPHIC_3,
663 &game.panel.graphic[2],
667 GAME_PANEL_GRAPHIC_4,
668 &game.panel.graphic[3],
672 GAME_PANEL_GRAPHIC_5,
673 &game.panel.graphic[4],
677 GAME_PANEL_GRAPHIC_6,
678 &game.panel.graphic[5],
682 GAME_PANEL_GRAPHIC_7,
683 &game.panel.graphic[6],
687 GAME_PANEL_GRAPHIC_8,
688 &game.panel.graphic[7],
692 GAME_PANEL_ELEMENT_1,
693 &game.panel.element[0],
697 GAME_PANEL_ELEMENT_2,
698 &game.panel.element[1],
702 GAME_PANEL_ELEMENT_3,
703 &game.panel.element[2],
707 GAME_PANEL_ELEMENT_4,
708 &game.panel.element[3],
712 GAME_PANEL_ELEMENT_5,
713 &game.panel.element[4],
717 GAME_PANEL_ELEMENT_6,
718 &game.panel.element[5],
722 GAME_PANEL_ELEMENT_7,
723 &game.panel.element[6],
727 GAME_PANEL_ELEMENT_8,
728 &game.panel.element[7],
732 GAME_PANEL_ELEMENT_COUNT_1,
733 &game.panel.element_count[0],
737 GAME_PANEL_ELEMENT_COUNT_2,
738 &game.panel.element_count[1],
742 GAME_PANEL_ELEMENT_COUNT_3,
743 &game.panel.element_count[2],
747 GAME_PANEL_ELEMENT_COUNT_4,
748 &game.panel.element_count[3],
752 GAME_PANEL_ELEMENT_COUNT_5,
753 &game.panel.element_count[4],
757 GAME_PANEL_ELEMENT_COUNT_6,
758 &game.panel.element_count[5],
762 GAME_PANEL_ELEMENT_COUNT_7,
763 &game.panel.element_count[6],
767 GAME_PANEL_ELEMENT_COUNT_8,
768 &game.panel.element_count[7],
772 GAME_PANEL_CE_SCORE_1,
773 &game.panel.ce_score[0],
777 GAME_PANEL_CE_SCORE_2,
778 &game.panel.ce_score[1],
782 GAME_PANEL_CE_SCORE_3,
783 &game.panel.ce_score[2],
787 GAME_PANEL_CE_SCORE_4,
788 &game.panel.ce_score[3],
792 GAME_PANEL_CE_SCORE_5,
793 &game.panel.ce_score[4],
797 GAME_PANEL_CE_SCORE_6,
798 &game.panel.ce_score[5],
802 GAME_PANEL_CE_SCORE_7,
803 &game.panel.ce_score[6],
807 GAME_PANEL_CE_SCORE_8,
808 &game.panel.ce_score[7],
812 GAME_PANEL_CE_SCORE_1_ELEMENT,
813 &game.panel.ce_score_element[0],
817 GAME_PANEL_CE_SCORE_2_ELEMENT,
818 &game.panel.ce_score_element[1],
822 GAME_PANEL_CE_SCORE_3_ELEMENT,
823 &game.panel.ce_score_element[2],
827 GAME_PANEL_CE_SCORE_4_ELEMENT,
828 &game.panel.ce_score_element[3],
832 GAME_PANEL_CE_SCORE_5_ELEMENT,
833 &game.panel.ce_score_element[4],
837 GAME_PANEL_CE_SCORE_6_ELEMENT,
838 &game.panel.ce_score_element[5],
842 GAME_PANEL_CE_SCORE_7_ELEMENT,
843 &game.panel.ce_score_element[6],
847 GAME_PANEL_CE_SCORE_8_ELEMENT,
848 &game.panel.ce_score_element[7],
852 GAME_PANEL_PLAYER_NAME,
853 &game.panel.player_name,
857 GAME_PANEL_LEVEL_NAME,
858 &game.panel.level_name,
862 GAME_PANEL_LEVEL_AUTHOR,
863 &game.panel.level_author,
876 /* values for delayed check of falling and moving elements and for collision */
877 #define CHECK_DELAY_MOVING 3
878 #define CHECK_DELAY_FALLING CHECK_DELAY_MOVING
879 #define CHECK_DELAY_COLLISION 2
880 #define CHECK_DELAY_IMPACT CHECK_DELAY_COLLISION
882 /* values for initial player move delay (initial delay counter value) */
883 #define INITIAL_MOVE_DELAY_OFF -1
884 #define INITIAL_MOVE_DELAY_ON 0
886 /* values for player movement speed (which is in fact a delay value) */
887 #define MOVE_DELAY_MIN_SPEED 32
888 #define MOVE_DELAY_NORMAL_SPEED 8
889 #define MOVE_DELAY_HIGH_SPEED 4
890 #define MOVE_DELAY_MAX_SPEED 1
892 #define DOUBLE_MOVE_DELAY(x) (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
893 #define HALVE_MOVE_DELAY(x) (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
895 #define DOUBLE_PLAYER_SPEED(p) (HALVE_MOVE_DELAY( (p)->move_delay_value))
896 #define HALVE_PLAYER_SPEED(p) (DOUBLE_MOVE_DELAY((p)->move_delay_value))
898 /* values for other actions */
899 #define MOVE_STEPSIZE_NORMAL (TILEX / MOVE_DELAY_NORMAL_SPEED)
900 #define MOVE_STEPSIZE_MIN (1)
901 #define MOVE_STEPSIZE_MAX (TILEX)
903 #define GET_DX_FROM_DIR(d) ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
904 #define GET_DY_FROM_DIR(d) ((d) == MV_UP ? -1 : (d) == MV_DOWN ? 1 : 0)
906 #define INIT_GFX_RANDOM() (GetSimpleRandom(1000000))
908 #define GET_NEW_PUSH_DELAY(e) ( (element_info[e].push_delay_fixed) + \
909 RND(element_info[e].push_delay_random))
910 #define GET_NEW_DROP_DELAY(e) ( (element_info[e].drop_delay_fixed) + \
911 RND(element_info[e].drop_delay_random))
912 #define GET_NEW_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
913 RND(element_info[e].move_delay_random))
914 #define GET_MAX_MOVE_DELAY(e) ( (element_info[e].move_delay_fixed) + \
915 (element_info[e].move_delay_random))
916 #define GET_NEW_CE_VALUE(e) ( (element_info[e].ce_value_fixed_initial) +\
917 RND(element_info[e].ce_value_random_initial))
918 #define GET_CE_SCORE(e) ( (element_info[e].collect_score))
919 #define GET_CHANGE_DELAY(c) ( ((c)->delay_fixed * (c)->delay_frames) + \
920 RND((c)->delay_random * (c)->delay_frames))
921 #define GET_CE_DELAY_VALUE(c) ( ((c)->delay_fixed) + \
922 RND((c)->delay_random))
925 #define GET_VALID_RUNTIME_ELEMENT(e) \
926 ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
928 #define RESOLVED_REFERENCE_ELEMENT(be, e) \
929 ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START : \
930 (be) + (e) - EL_SELF > EL_CUSTOM_END ? EL_CUSTOM_END : \
931 (be) + (e) - EL_SELF)
933 #define GET_PLAYER_FROM_BITS(p) \
934 (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
936 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs) \
937 ((e) == EL_TRIGGER_PLAYER ? (ch)->actual_trigger_player : \
938 (e) == EL_TRIGGER_ELEMENT ? (ch)->actual_trigger_element : \
939 (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value : \
940 (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score : \
941 (e) == EL_CURRENT_CE_VALUE ? (cv) : \
942 (e) == EL_CURRENT_CE_SCORE ? (cs) : \
943 (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ? \
944 RESOLVED_REFERENCE_ELEMENT(be, e) : \
947 #define CAN_GROW_INTO(e) \
948 ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
950 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition) \
951 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition) \
955 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
956 (CAN_MOVE_INTO_ACID(e) && \
957 Feld[x][y] == EL_ACID) || \
960 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition) \
961 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) || \
962 (CAN_MOVE_INTO_ACID(e) && \
963 Feld[x][y] == EL_ACID) || \
966 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition) \
967 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) || \
969 (CAN_MOVE_INTO_ACID(e) && \
970 Feld[x][y] == EL_ACID) || \
971 (DONT_COLLIDE_WITH(e) && \
973 !PLAYER_ENEMY_PROTECTED(x, y))))
975 #define ELEMENT_CAN_ENTER_FIELD(e, x, y) \
976 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
978 #define SATELLITE_CAN_ENTER_FIELD(x, y) \
979 ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
981 #define ANDROID_CAN_ENTER_FIELD(e, x, y) \
982 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
984 #define ANDROID_CAN_CLONE_FIELD(x, y) \
985 (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
986 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
988 #define ENEMY_CAN_ENTER_FIELD(e, x, y) \
989 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
991 #define YAMYAM_CAN_ENTER_FIELD(e, x, y) \
992 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
994 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y) \
995 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
997 #define PACMAN_CAN_ENTER_FIELD(e, x, y) \
998 ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
1000 #define PIG_CAN_ENTER_FIELD(e, x, y) \
1001 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
1003 #define PENGUIN_CAN_ENTER_FIELD(e, x, y) \
1004 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
1005 Feld[x][y] == EL_EM_EXIT_OPEN || \
1006 Feld[x][y] == EL_STEEL_EXIT_OPEN || \
1007 Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
1008 IS_FOOD_PENGUIN(Feld[x][y])))
1009 #define DRAGON_CAN_ENTER_FIELD(e, x, y) \
1010 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1012 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition) \
1013 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1015 #define SPRING_CAN_ENTER_FIELD(e, x, y) \
1016 ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1018 #define SPRING_CAN_BUMP_FROM_FIELD(x, y) \
1019 (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER || \
1020 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1022 #define MOVE_ENTER_EL(e) (element_info[e].move_enter_element)
1024 #define CE_ENTER_FIELD_COND(e, x, y) \
1025 (!IS_PLAYER(x, y) && \
1026 IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
1028 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y) \
1029 ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1031 #define IN_LEV_FIELD_AND_IS_FREE(x, y) (IN_LEV_FIELD(x, y) && IS_FREE(x, y))
1032 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1034 #define ACCESS_FROM(e, d) (element_info[e].access_direction &(d))
1035 #define IS_WALKABLE_FROM(e, d) (IS_WALKABLE(e) && ACCESS_FROM(e, d))
1036 #define IS_PASSABLE_FROM(e, d) (IS_PASSABLE(e) && ACCESS_FROM(e, d))
1037 #define IS_ACCESSIBLE_FROM(e, d) (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1039 /* game button identifiers */
1040 #define GAME_CTRL_ID_STOP 0
1041 #define GAME_CTRL_ID_PAUSE 1
1042 #define GAME_CTRL_ID_PLAY 2
1043 #define SOUND_CTRL_ID_MUSIC 3
1044 #define SOUND_CTRL_ID_LOOPS 4
1045 #define SOUND_CTRL_ID_SIMPLE 5
1047 #define NUM_GAME_BUTTONS 6
1050 /* forward declaration for internal use */
1052 static void CreateField(int, int, int);
1054 static void ResetGfxAnimation(int, int);
1056 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1057 static void AdvanceFrameAndPlayerCounters(int);
1059 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1060 static boolean MovePlayer(struct PlayerInfo *, int, int);
1061 static void ScrollPlayer(struct PlayerInfo *, int);
1062 static void ScrollScreen(struct PlayerInfo *, int);
1064 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1065 static boolean DigFieldByCE(int, int, int);
1066 static boolean SnapField(struct PlayerInfo *, int, int);
1067 static boolean DropElement(struct PlayerInfo *);
1069 static void InitBeltMovement(void);
1070 static void CloseAllOpenTimegates(void);
1071 static void CheckGravityMovement(struct PlayerInfo *);
1072 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1073 static void KillPlayerUnlessEnemyProtected(int, int);
1074 static void KillPlayerUnlessExplosionProtected(int, int);
1076 static void TestIfPlayerTouchesCustomElement(int, int);
1077 static void TestIfElementTouchesCustomElement(int, int);
1078 static void TestIfElementHitsCustomElement(int, int, int);
1080 static void TestIfElementSmashesCustomElement(int, int, int);
1083 static void HandleElementChange(int, int, int);
1084 static void ExecuteCustomElementAction(int, int, int, int);
1085 static boolean ChangeElement(int, int, int, int);
1087 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1088 #define CheckTriggeredElementChange(x, y, e, ev) \
1089 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1090 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s) \
1091 CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1092 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s) \
1093 CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1094 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p) \
1095 CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1097 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1098 #define CheckElementChange(x, y, e, te, ev) \
1099 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1100 #define CheckElementChangeByPlayer(x, y, e, ev, p, s) \
1101 CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1102 #define CheckElementChangeBySide(x, y, e, te, ev, s) \
1103 CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1105 static void PlayLevelSound(int, int, int);
1106 static void PlayLevelSoundNearest(int, int, int);
1107 static void PlayLevelSoundAction(int, int, int);
1108 static void PlayLevelSoundElementAction(int, int, int, int);
1109 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1110 static void PlayLevelSoundActionIfLoop(int, int, int);
1111 static void StopLevelSoundActionIfLoop(int, int, int);
1112 static void PlayLevelMusic();
1114 static void MapGameButtons();
1115 static void HandleGameButtons(struct GadgetInfo *);
1117 int AmoebeNachbarNr(int, int);
1118 void AmoebeUmwandeln(int, int);
1119 void ContinueMoving(int, int);
1120 void Bang(int, int);
1121 void InitMovDir(int, int);
1122 void InitAmoebaNr(int, int);
1123 int NewHiScore(void);
1125 void TestIfGoodThingHitsBadThing(int, int, int);
1126 void TestIfBadThingHitsGoodThing(int, int, int);
1127 void TestIfPlayerTouchesBadThing(int, int);
1128 void TestIfPlayerRunsIntoBadThing(int, int, int);
1129 void TestIfBadThingTouchesPlayer(int, int);
1130 void TestIfBadThingRunsIntoPlayer(int, int, int);
1131 void TestIfFriendTouchesBadThing(int, int);
1132 void TestIfBadThingTouchesFriend(int, int);
1133 void TestIfBadThingTouchesOtherBadThing(int, int);
1134 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1136 void KillPlayer(struct PlayerInfo *);
1137 void BuryPlayer(struct PlayerInfo *);
1138 void RemovePlayer(struct PlayerInfo *);
1140 static int getInvisibleActiveFromInvisibleElement(int);
1141 static int getInvisibleFromInvisibleActiveElement(int);
1143 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1145 /* for detection of endless loops, caused by custom element programming */
1146 /* (using maximal playfield width x 10 is just a rough approximation) */
1147 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH (MAX_PLAYFIELD_WIDTH * 10)
1149 #define RECURSION_LOOP_DETECTION_START(e, rc) \
1151 if (recursion_loop_detected) \
1154 if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH) \
1156 recursion_loop_detected = TRUE; \
1157 recursion_loop_element = (e); \
1160 recursion_loop_depth++; \
1163 #define RECURSION_LOOP_DETECTION_END() \
1165 recursion_loop_depth--; \
1168 static int recursion_loop_depth;
1169 static boolean recursion_loop_detected;
1170 static boolean recursion_loop_element;
1172 static int map_player_action[MAX_PLAYERS];
1175 /* ------------------------------------------------------------------------- */
1176 /* definition of elements that automatically change to other elements after */
1177 /* a specified time, eventually calling a function when changing */
1178 /* ------------------------------------------------------------------------- */
1180 /* forward declaration for changer functions */
1181 static void InitBuggyBase(int, int);
1182 static void WarnBuggyBase(int, int);
1184 static void InitTrap(int, int);
1185 static void ActivateTrap(int, int);
1186 static void ChangeActiveTrap(int, int);
1188 static void InitRobotWheel(int, int);
1189 static void RunRobotWheel(int, int);
1190 static void StopRobotWheel(int, int);
1192 static void InitTimegateWheel(int, int);
1193 static void RunTimegateWheel(int, int);
1195 static void InitMagicBallDelay(int, int);
1196 static void ActivateMagicBall(int, int);
1198 struct ChangingElementInfo
1203 void (*pre_change_function)(int x, int y);
1204 void (*change_function)(int x, int y);
1205 void (*post_change_function)(int x, int y);
1208 static struct ChangingElementInfo change_delay_list[] =
1243 EL_STEEL_EXIT_OPENING,
1251 EL_STEEL_EXIT_CLOSING,
1252 EL_STEEL_EXIT_CLOSED,
1279 EL_EM_STEEL_EXIT_OPENING,
1280 EL_EM_STEEL_EXIT_OPEN,
1287 EL_EM_STEEL_EXIT_CLOSING,
1291 EL_EM_STEEL_EXIT_CLOSED,
1315 EL_SWITCHGATE_OPENING,
1323 EL_SWITCHGATE_CLOSING,
1324 EL_SWITCHGATE_CLOSED,
1331 EL_TIMEGATE_OPENING,
1339 EL_TIMEGATE_CLOSING,
1348 EL_ACID_SPLASH_LEFT,
1356 EL_ACID_SPLASH_RIGHT,
1365 EL_SP_BUGGY_BASE_ACTIVATING,
1372 EL_SP_BUGGY_BASE_ACTIVATING,
1373 EL_SP_BUGGY_BASE_ACTIVE,
1380 EL_SP_BUGGY_BASE_ACTIVE,
1404 EL_ROBOT_WHEEL_ACTIVE,
1412 EL_TIMEGATE_SWITCH_ACTIVE,
1420 EL_DC_TIMEGATE_SWITCH_ACTIVE,
1421 EL_DC_TIMEGATE_SWITCH,
1428 EL_EMC_MAGIC_BALL_ACTIVE,
1429 EL_EMC_MAGIC_BALL_ACTIVE,
1436 EL_EMC_SPRING_BUMPER_ACTIVE,
1437 EL_EMC_SPRING_BUMPER,
1444 EL_DIAGONAL_SHRINKING,
1452 EL_DIAGONAL_GROWING,
1473 int push_delay_fixed, push_delay_random;
1477 { EL_SPRING, 0, 0 },
1478 { EL_BALLOON, 0, 0 },
1480 { EL_SOKOBAN_OBJECT, 2, 0 },
1481 { EL_SOKOBAN_FIELD_FULL, 2, 0 },
1482 { EL_SATELLITE, 2, 0 },
1483 { EL_SP_DISK_YELLOW, 2, 0 },
1485 { EL_UNDEFINED, 0, 0 },
1493 move_stepsize_list[] =
1495 { EL_AMOEBA_DROP, 2 },
1496 { EL_AMOEBA_DROPPING, 2 },
1497 { EL_QUICKSAND_FILLING, 1 },
1498 { EL_QUICKSAND_EMPTYING, 1 },
1499 { EL_QUICKSAND_FAST_FILLING, 2 },
1500 { EL_QUICKSAND_FAST_EMPTYING, 2 },
1501 { EL_MAGIC_WALL_FILLING, 2 },
1502 { EL_MAGIC_WALL_EMPTYING, 2 },
1503 { EL_BD_MAGIC_WALL_FILLING, 2 },
1504 { EL_BD_MAGIC_WALL_EMPTYING, 2 },
1505 { EL_DC_MAGIC_WALL_FILLING, 2 },
1506 { EL_DC_MAGIC_WALL_EMPTYING, 2 },
1508 { EL_UNDEFINED, 0 },
1516 collect_count_list[] =
1519 { EL_BD_DIAMOND, 1 },
1520 { EL_EMERALD_YELLOW, 1 },
1521 { EL_EMERALD_RED, 1 },
1522 { EL_EMERALD_PURPLE, 1 },
1524 { EL_SP_INFOTRON, 1 },
1528 { EL_UNDEFINED, 0 },
1536 access_direction_list[] =
1538 { EL_TUBE_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1539 { EL_TUBE_VERTICAL, MV_UP | MV_DOWN },
1540 { EL_TUBE_HORIZONTAL, MV_LEFT | MV_RIGHT },
1541 { EL_TUBE_VERTICAL_LEFT, MV_LEFT | MV_UP | MV_DOWN },
1542 { EL_TUBE_VERTICAL_RIGHT, MV_RIGHT | MV_UP | MV_DOWN },
1543 { EL_TUBE_HORIZONTAL_UP, MV_LEFT | MV_RIGHT | MV_UP },
1544 { EL_TUBE_HORIZONTAL_DOWN, MV_LEFT | MV_RIGHT | MV_DOWN },
1545 { EL_TUBE_LEFT_UP, MV_LEFT | MV_UP },
1546 { EL_TUBE_LEFT_DOWN, MV_LEFT | MV_DOWN },
1547 { EL_TUBE_RIGHT_UP, MV_RIGHT | MV_UP },
1548 { EL_TUBE_RIGHT_DOWN, MV_RIGHT | MV_DOWN },
1550 { EL_SP_PORT_LEFT, MV_RIGHT },
1551 { EL_SP_PORT_RIGHT, MV_LEFT },
1552 { EL_SP_PORT_UP, MV_DOWN },
1553 { EL_SP_PORT_DOWN, MV_UP },
1554 { EL_SP_PORT_HORIZONTAL, MV_LEFT | MV_RIGHT },
1555 { EL_SP_PORT_VERTICAL, MV_UP | MV_DOWN },
1556 { EL_SP_PORT_ANY, MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1557 { EL_SP_GRAVITY_PORT_LEFT, MV_RIGHT },
1558 { EL_SP_GRAVITY_PORT_RIGHT, MV_LEFT },
1559 { EL_SP_GRAVITY_PORT_UP, MV_DOWN },
1560 { EL_SP_GRAVITY_PORT_DOWN, MV_UP },
1561 { EL_SP_GRAVITY_ON_PORT_LEFT, MV_RIGHT },
1562 { EL_SP_GRAVITY_ON_PORT_RIGHT, MV_LEFT },
1563 { EL_SP_GRAVITY_ON_PORT_UP, MV_DOWN },
1564 { EL_SP_GRAVITY_ON_PORT_DOWN, MV_UP },
1565 { EL_SP_GRAVITY_OFF_PORT_LEFT, MV_RIGHT },
1566 { EL_SP_GRAVITY_OFF_PORT_RIGHT, MV_LEFT },
1567 { EL_SP_GRAVITY_OFF_PORT_UP, MV_DOWN },
1568 { EL_SP_GRAVITY_OFF_PORT_DOWN, MV_UP },
1570 { EL_UNDEFINED, MV_NONE }
1573 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1575 #define IS_AUTO_CHANGING(e) (element_info[e].has_change_event[CE_DELAY])
1576 #define IS_JUST_CHANGING(x, y) (ChangeDelay[x][y] != 0)
1577 #define IS_CHANGING(x, y) (IS_AUTO_CHANGING(Feld[x][y]) || \
1578 IS_JUST_CHANGING(x, y))
1580 #define CE_PAGE(e, ce) (element_info[e].event_page[ce])
1582 /* static variables for playfield scan mode (scanning forward or backward) */
1583 static int playfield_scan_start_x = 0;
1584 static int playfield_scan_start_y = 0;
1585 static int playfield_scan_delta_x = 1;
1586 static int playfield_scan_delta_y = 1;
1588 #define SCAN_PLAYFIELD(x, y) for ((y) = playfield_scan_start_y; \
1589 (y) >= 0 && (y) <= lev_fieldy - 1; \
1590 (y) += playfield_scan_delta_y) \
1591 for ((x) = playfield_scan_start_x; \
1592 (x) >= 0 && (x) <= lev_fieldx - 1; \
1593 (x) += playfield_scan_delta_x)
1596 void DEBUG_SetMaximumDynamite()
1600 for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1601 if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1602 local_player->inventory_element[local_player->inventory_size++] =
1607 static void InitPlayfieldScanModeVars()
1609 if (game.use_reverse_scan_direction)
1611 playfield_scan_start_x = lev_fieldx - 1;
1612 playfield_scan_start_y = lev_fieldy - 1;
1614 playfield_scan_delta_x = -1;
1615 playfield_scan_delta_y = -1;
1619 playfield_scan_start_x = 0;
1620 playfield_scan_start_y = 0;
1622 playfield_scan_delta_x = 1;
1623 playfield_scan_delta_y = 1;
1627 static void InitPlayfieldScanMode(int mode)
1629 game.use_reverse_scan_direction =
1630 (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1632 InitPlayfieldScanModeVars();
1635 static int get_move_delay_from_stepsize(int move_stepsize)
1638 MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1640 /* make sure that stepsize value is always a power of 2 */
1641 move_stepsize = (1 << log_2(move_stepsize));
1643 return TILEX / move_stepsize;
1646 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1649 int player_nr = player->index_nr;
1650 int move_delay = get_move_delay_from_stepsize(move_stepsize);
1651 boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1653 /* do no immediately change move delay -- the player might just be moving */
1654 player->move_delay_value_next = move_delay;
1656 /* information if player can move must be set separately */
1657 player->cannot_move = cannot_move;
1661 player->move_delay = game.initial_move_delay[player_nr];
1662 player->move_delay_value = game.initial_move_delay_value[player_nr];
1664 player->move_delay_value_next = -1;
1666 player->move_delay_reset_counter = 0;
1670 void GetPlayerConfig()
1672 GameFrameDelay = setup.game_frame_delay;
1674 if (!audio.sound_available)
1675 setup.sound_simple = FALSE;
1677 if (!audio.loops_available)
1678 setup.sound_loops = FALSE;
1680 if (!audio.music_available)
1681 setup.sound_music = FALSE;
1683 if (!video.fullscreen_available)
1684 setup.fullscreen = FALSE;
1686 setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1688 SetAudioMode(setup.sound);
1692 int GetElementFromGroupElement(int element)
1694 if (IS_GROUP_ELEMENT(element))
1696 struct ElementGroupInfo *group = element_info[element].group;
1697 int last_anim_random_frame = gfx.anim_random_frame;
1700 if (group->choice_mode == ANIM_RANDOM)
1701 gfx.anim_random_frame = RND(group->num_elements_resolved);
1703 element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1704 group->choice_mode, 0,
1707 if (group->choice_mode == ANIM_RANDOM)
1708 gfx.anim_random_frame = last_anim_random_frame;
1710 group->choice_pos++;
1712 element = group->element_resolved[element_pos];
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1720 if (element == EL_SP_MURPHY)
1724 if (stored_player[0].present)
1726 Feld[x][y] = EL_SP_MURPHY_CLONE;
1732 stored_player[0].initial_element = element;
1733 stored_player[0].use_murphy = TRUE;
1735 if (!level.use_artwork_element[0])
1736 stored_player[0].artwork_element = EL_SP_MURPHY;
1739 Feld[x][y] = EL_PLAYER_1;
1745 struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1746 int jx = player->jx, jy = player->jy;
1748 player->present = TRUE;
1750 player->block_last_field = (element == EL_SP_MURPHY ?
1751 level.sp_block_last_field :
1752 level.block_last_field);
1754 /* ---------- initialize player's last field block delay --------------- */
1756 /* always start with reliable default value (no adjustment needed) */
1757 player->block_delay_adjustment = 0;
1759 /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1760 if (player->block_last_field && element == EL_SP_MURPHY)
1761 player->block_delay_adjustment = 1;
1763 /* special case 2: in game engines before 3.1.1, blocking was different */
1764 if (game.use_block_last_field_bug)
1765 player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1767 if (!options.network || player->connected)
1769 player->active = TRUE;
1771 /* remove potentially duplicate players */
1772 if (StorePlayer[jx][jy] == Feld[x][y])
1773 StorePlayer[jx][jy] = 0;
1775 StorePlayer[x][y] = Feld[x][y];
1779 printf("Player %d activated.\n", player->element_nr);
1780 printf("[Local player is %d and currently %s.]\n",
1781 local_player->element_nr,
1782 local_player->active ? "active" : "not active");
1786 Feld[x][y] = EL_EMPTY;
1788 player->jx = player->last_jx = x;
1789 player->jy = player->last_jy = y;
1792 #if USE_PLAYER_REANIMATION
1795 int player_nr = GET_PLAYER_NR(element);
1796 struct PlayerInfo *player = &stored_player[player_nr];
1798 if (player->active && player->killed)
1799 player->reanimated = TRUE; /* if player was just killed, reanimate him */
1804 static void InitField(int x, int y, boolean init_game)
1806 int element = Feld[x][y];
1815 InitPlayerField(x, y, element, init_game);
1818 case EL_SOKOBAN_FIELD_PLAYER:
1819 element = Feld[x][y] = EL_PLAYER_1;
1820 InitField(x, y, init_game);
1822 element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1823 InitField(x, y, init_game);
1826 case EL_SOKOBAN_FIELD_EMPTY:
1827 local_player->sokobanfields_still_needed++;
1831 if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1832 Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1833 else if (x > 0 && Feld[x-1][y] == EL_ACID)
1834 Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1835 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1836 Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1837 else if (y > 0 && Feld[x][y-1] == EL_ACID)
1838 Feld[x][y] = EL_ACID_POOL_BOTTOM;
1839 else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1840 Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1849 case EL_SPACESHIP_RIGHT:
1850 case EL_SPACESHIP_UP:
1851 case EL_SPACESHIP_LEFT:
1852 case EL_SPACESHIP_DOWN:
1853 case EL_BD_BUTTERFLY:
1854 case EL_BD_BUTTERFLY_RIGHT:
1855 case EL_BD_BUTTERFLY_UP:
1856 case EL_BD_BUTTERFLY_LEFT:
1857 case EL_BD_BUTTERFLY_DOWN:
1859 case EL_BD_FIREFLY_RIGHT:
1860 case EL_BD_FIREFLY_UP:
1861 case EL_BD_FIREFLY_LEFT:
1862 case EL_BD_FIREFLY_DOWN:
1863 case EL_PACMAN_RIGHT:
1865 case EL_PACMAN_LEFT:
1866 case EL_PACMAN_DOWN:
1868 case EL_YAMYAM_LEFT:
1869 case EL_YAMYAM_RIGHT:
1871 case EL_YAMYAM_DOWN:
1872 case EL_DARK_YAMYAM:
1875 case EL_SP_SNIKSNAK:
1876 case EL_SP_ELECTRON:
1885 case EL_AMOEBA_FULL:
1890 case EL_AMOEBA_DROP:
1891 if (y == lev_fieldy - 1)
1893 Feld[x][y] = EL_AMOEBA_GROWING;
1894 Store[x][y] = EL_AMOEBA_WET;
1898 case EL_DYNAMITE_ACTIVE:
1899 case EL_SP_DISK_RED_ACTIVE:
1900 case EL_DYNABOMB_PLAYER_1_ACTIVE:
1901 case EL_DYNABOMB_PLAYER_2_ACTIVE:
1902 case EL_DYNABOMB_PLAYER_3_ACTIVE:
1903 case EL_DYNABOMB_PLAYER_4_ACTIVE:
1904 MovDelay[x][y] = 96;
1907 case EL_EM_DYNAMITE_ACTIVE:
1908 MovDelay[x][y] = 32;
1912 local_player->lights_still_needed++;
1916 local_player->friends_still_needed++;
1921 GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1924 case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1925 case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1926 case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1927 case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1928 case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1929 case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1930 case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1931 case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1932 case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1933 case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1934 case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1935 case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1938 int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1939 int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1940 int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1942 if (game.belt_dir_nr[belt_nr] == 3) /* initial value */
1944 game.belt_dir[belt_nr] = belt_dir;
1945 game.belt_dir_nr[belt_nr] = belt_dir_nr;
1947 else /* more than one switch -- set it like the first switch */
1949 Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1954 #if !USE_BOTH_SWITCHGATE_SWITCHES
1955 case EL_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1957 Feld[x][y] = EL_SWITCHGATE_SWITCH_UP;
1960 case EL_DC_SWITCHGATE_SWITCH_DOWN: /* always start with same switch pos */
1962 Feld[x][y] = EL_DC_SWITCHGATE_SWITCH_UP;
1966 case EL_LIGHT_SWITCH_ACTIVE:
1968 game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1971 case EL_INVISIBLE_STEELWALL:
1972 case EL_INVISIBLE_WALL:
1973 case EL_INVISIBLE_SAND:
1974 if (game.light_time_left > 0 ||
1975 game.lenses_time_left > 0)
1976 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1979 case EL_EMC_MAGIC_BALL:
1980 if (game.ball_state)
1981 Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1984 case EL_EMC_MAGIC_BALL_SWITCH:
1985 if (game.ball_state)
1986 Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1989 case EL_TRIGGER_PLAYER:
1990 case EL_TRIGGER_ELEMENT:
1991 case EL_TRIGGER_CE_VALUE:
1992 case EL_TRIGGER_CE_SCORE:
1994 case EL_ANY_ELEMENT:
1995 case EL_CURRENT_CE_VALUE:
1996 case EL_CURRENT_CE_SCORE:
2013 /* reference elements should not be used on the playfield */
2014 Feld[x][y] = EL_EMPTY;
2018 if (IS_CUSTOM_ELEMENT(element))
2020 if (CAN_MOVE(element))
2023 #if USE_NEW_CUSTOM_VALUE
2024 if (!element_info[element].use_last_ce_value || init_game)
2025 CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
2028 else if (IS_GROUP_ELEMENT(element))
2030 Feld[x][y] = GetElementFromGroupElement(element);
2032 InitField(x, y, init_game);
2039 CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2042 static inline void InitField_WithBug1(int x, int y, boolean init_game)
2044 InitField(x, y, init_game);
2046 /* not needed to call InitMovDir() -- already done by InitField()! */
2047 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2048 CAN_MOVE(Feld[x][y]))
2052 static inline void InitField_WithBug2(int x, int y, boolean init_game)
2054 int old_element = Feld[x][y];
2056 InitField(x, y, init_game);
2058 /* not needed to call InitMovDir() -- already done by InitField()! */
2059 if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2060 CAN_MOVE(old_element) &&
2061 (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2064 /* this case is in fact a combination of not less than three bugs:
2065 first, it calls InitMovDir() for elements that can move, although this is
2066 already done by InitField(); then, it checks the element that was at this
2067 field _before_ the call to InitField() (which can change it); lastly, it
2068 was not called for "mole with direction" elements, which were treated as
2069 "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2075 static int get_key_element_from_nr(int key_nr)
2077 int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2078 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2079 EL_EM_KEY_1 : EL_KEY_1);
2081 return key_base_element + key_nr;
2084 static int get_next_dropped_element(struct PlayerInfo *player)
2086 return (player->inventory_size > 0 ?
2087 player->inventory_element[player->inventory_size - 1] :
2088 player->inventory_infinite_element != EL_UNDEFINED ?
2089 player->inventory_infinite_element :
2090 player->dynabombs_left > 0 ?
2091 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2095 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2097 /* pos >= 0: get element from bottom of the stack;
2098 pos < 0: get element from top of the stack */
2102 int min_inventory_size = -pos;
2103 int inventory_pos = player->inventory_size - min_inventory_size;
2104 int min_dynabombs_left = min_inventory_size - player->inventory_size;
2106 return (player->inventory_size >= min_inventory_size ?
2107 player->inventory_element[inventory_pos] :
2108 player->inventory_infinite_element != EL_UNDEFINED ?
2109 player->inventory_infinite_element :
2110 player->dynabombs_left >= min_dynabombs_left ?
2111 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116 int min_dynabombs_left = pos + 1;
2117 int min_inventory_size = pos + 1 - player->dynabombs_left;
2118 int inventory_pos = pos - player->dynabombs_left;
2120 return (player->inventory_infinite_element != EL_UNDEFINED ?
2121 player->inventory_infinite_element :
2122 player->dynabombs_left >= min_dynabombs_left ?
2123 EL_DYNABOMB_PLAYER_1 + player->index_nr :
2124 player->inventory_size >= min_inventory_size ?
2125 player->inventory_element[inventory_pos] :
2130 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2132 const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2133 const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2136 if (gpo1->sort_priority != gpo2->sort_priority)
2137 compare_result = gpo1->sort_priority - gpo2->sort_priority;
2139 compare_result = gpo1->nr - gpo2->nr;
2141 return compare_result;
2144 void InitGameControlValues()
2148 for (i = 0; game_panel_controls[i].nr != -1; i++)
2150 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2151 struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2152 struct TextPosInfo *pos = gpc->pos;
2154 int type = gpc->type;
2158 Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2159 Error(ERR_EXIT, "this should not happen -- please debug");
2162 /* force update of game controls after initialization */
2163 gpc->value = gpc->last_value = -1;
2164 gpc->frame = gpc->last_frame = -1;
2165 gpc->gfx_frame = -1;
2167 /* determine panel value width for later calculation of alignment */
2168 if (type == TYPE_INTEGER || type == TYPE_STRING)
2170 pos->width = pos->size * getFontWidth(pos->font);
2171 pos->height = getFontHeight(pos->font);
2173 else if (type == TYPE_ELEMENT)
2175 pos->width = pos->size;
2176 pos->height = pos->size;
2179 /* fill structure for game panel draw order */
2181 gpo->sort_priority = pos->sort_priority;
2184 /* sort game panel controls according to sort_priority and control number */
2185 qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2186 sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2189 void UpdatePlayfieldElementCount()
2191 boolean use_element_count = FALSE;
2194 /* first check if it is needed at all to calculate playfield element count */
2195 for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2196 if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2197 use_element_count = TRUE;
2199 if (!use_element_count)
2202 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2203 element_info[i].element_count = 0;
2205 SCAN_PLAYFIELD(x, y)
2207 element_info[Feld[x][y]].element_count++;
2210 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2211 for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2212 if (IS_IN_GROUP(j, i))
2213 element_info[EL_GROUP_START + i].element_count +=
2214 element_info[j].element_count;
2217 void UpdateGameControlValues()
2220 int time = (local_player->LevelSolved ?
2221 local_player->LevelSolved_CountingTime :
2222 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2223 level.native_em_level->lev->time :
2224 level.time == 0 ? TimePlayed : TimeLeft);
2225 int score = (local_player->LevelSolved ?
2226 local_player->LevelSolved_CountingScore :
2227 level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2228 level.native_em_level->lev->score :
2229 local_player->score);
2230 int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231 level.native_em_level->lev->required :
2232 local_player->gems_still_needed);
2233 int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2234 level.native_em_level->lev->required > 0 :
2235 local_player->gems_still_needed > 0 ||
2236 local_player->sokobanfields_still_needed > 0 ||
2237 local_player->lights_still_needed > 0);
2239 UpdatePlayfieldElementCount();
2241 /* update game panel control values */
2243 game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2244 game_panel_controls[GAME_PANEL_GEMS].value = gems;
2246 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2247 for (i = 0; i < MAX_NUM_KEYS; i++)
2248 game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2249 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2250 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2252 if (game.centered_player_nr == -1)
2254 for (i = 0; i < MAX_PLAYERS; i++)
2256 for (k = 0; k < MAX_NUM_KEYS; k++)
2258 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2260 if (level.native_em_level->ply[i]->keys & (1 << k))
2261 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2262 get_key_element_from_nr(k);
2264 else if (stored_player[i].key[k])
2265 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2266 get_key_element_from_nr(k);
2269 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2270 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2271 level.native_em_level->ply[i]->dynamite;
2273 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2274 stored_player[i].inventory_size;
2276 if (stored_player[i].num_white_keys > 0)
2277 game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2280 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2281 stored_player[i].num_white_keys;
2286 int player_nr = game.centered_player_nr;
2288 for (k = 0; k < MAX_NUM_KEYS; k++)
2290 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2292 if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2293 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294 get_key_element_from_nr(k);
2296 else if (stored_player[player_nr].key[k])
2297 game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298 get_key_element_from_nr(k);
2301 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2302 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2303 level.native_em_level->ply[player_nr]->dynamite;
2305 game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2306 stored_player[player_nr].inventory_size;
2308 if (stored_player[player_nr].num_white_keys > 0)
2309 game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2311 game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2312 stored_player[player_nr].num_white_keys;
2315 for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2317 game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2318 get_inventory_element_from_pos(local_player, i);
2319 game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2320 get_inventory_element_from_pos(local_player, -i - 1);
2323 game_panel_controls[GAME_PANEL_SCORE].value = score;
2324 game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2326 game_panel_controls[GAME_PANEL_TIME].value = time;
2328 game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2329 game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2330 game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2332 game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2333 (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2335 game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2336 local_player->shield_normal_time_left;
2337 game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2338 (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2340 game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2341 local_player->shield_deadly_time_left;
2343 game_panel_controls[GAME_PANEL_EXIT].value =
2344 (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2346 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2347 (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2348 game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2349 (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2350 EL_EMC_MAGIC_BALL_SWITCH);
2352 game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2353 (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2354 game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2355 game.light_time_left;
2357 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2358 (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2359 game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2360 game.timegate_time_left;
2362 game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2363 EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2365 game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2366 (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2367 game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2368 game.lenses_time_left;
2370 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2371 (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2372 game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2373 game.magnify_time_left;
2375 game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2376 (game.wind_direction == MV_LEFT ? EL_BALLOON_SWITCH_LEFT :
2377 game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2378 game.wind_direction == MV_UP ? EL_BALLOON_SWITCH_UP :
2379 game.wind_direction == MV_DOWN ? EL_BALLOON_SWITCH_DOWN :
2380 EL_BALLOON_SWITCH_NONE);
2382 game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2383 local_player->dynabomb_count;
2384 game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2385 local_player->dynabomb_size;
2386 game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2387 (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2389 game_panel_controls[GAME_PANEL_PENGUINS].value =
2390 local_player->friends_still_needed;
2392 game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2393 local_player->sokobanfields_still_needed;
2394 game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2395 local_player->sokobanfields_still_needed;
2397 game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2398 (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2400 for (i = 0; i < NUM_BELTS; i++)
2402 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2403 (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2404 EL_CONVEYOR_BELT_1_MIDDLE) + i;
2405 game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2406 getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2409 game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2410 (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2411 game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2412 game.magic_wall_time_left;
2414 #if USE_PLAYER_GRAVITY
2415 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2416 local_player->gravity;
2418 game_panel_controls[GAME_PANEL_GRAVITY_STATE].value = game.gravity;
2421 for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2422 game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2424 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2425 game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2426 (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2427 game.panel.element[i].id : EL_UNDEFINED);
2429 for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2430 game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2431 (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2432 element_info[game.panel.element_count[i].id].element_count : 0);
2434 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2435 game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2436 (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2437 element_info[game.panel.ce_score[i].id].collect_score : 0);
2439 for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2440 game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2441 (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2442 element_info[game.panel.ce_score_element[i].id].collect_score :
2445 game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2446 game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2447 game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2449 /* update game panel control frames */
2451 for (i = 0; game_panel_controls[i].nr != -1; i++)
2453 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2455 if (gpc->type == TYPE_ELEMENT)
2457 if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2459 int last_anim_random_frame = gfx.anim_random_frame;
2460 int element = gpc->value;
2461 int graphic = el2panelimg(element);
2463 if (gpc->value != gpc->last_value)
2466 gpc->gfx_random = INIT_GFX_RANDOM();
2472 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2473 IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2474 gpc->gfx_random = INIT_GFX_RANDOM();
2477 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2478 gfx.anim_random_frame = gpc->gfx_random;
2480 if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2481 gpc->gfx_frame = element_info[element].collect_score;
2483 gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2486 if (ANIM_MODE(graphic) == ANIM_RANDOM)
2487 gfx.anim_random_frame = last_anim_random_frame;
2493 void DisplayGameControlValues()
2495 boolean redraw_panel = FALSE;
2498 for (i = 0; game_panel_controls[i].nr != -1; i++)
2500 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2502 if (PANEL_DEACTIVATED(gpc->pos))
2505 if (gpc->value == gpc->last_value &&
2506 gpc->frame == gpc->last_frame)
2509 redraw_panel = TRUE;
2515 /* copy default game door content to main double buffer */
2516 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
2517 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
2519 /* redraw game control buttons */
2521 RedrawGameButtons();
2527 game_status = GAME_MODE_PSEUDO_PANEL;
2530 for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2532 for (i = 0; game_panel_controls[i].nr != -1; i++)
2536 int nr = game_panel_order[i].nr;
2537 struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2539 struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2542 struct TextPosInfo *pos = gpc->pos;
2543 int type = gpc->type;
2544 int value = gpc->value;
2545 int frame = gpc->frame;
2547 int last_value = gpc->last_value;
2548 int last_frame = gpc->last_frame;
2550 int size = pos->size;
2551 int font = pos->font;
2552 boolean draw_masked = pos->draw_masked;
2553 int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2555 if (PANEL_DEACTIVATED(pos))
2559 if (value == last_value && frame == last_frame)
2563 gpc->last_value = value;
2564 gpc->last_frame = frame;
2567 printf("::: value %d changed from %d to %d\n", nr, last_value, value);
2570 if (type == TYPE_INTEGER)
2572 if (nr == GAME_PANEL_LEVEL_NUMBER ||
2573 nr == GAME_PANEL_TIME)
2575 boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2577 if (use_dynamic_size) /* use dynamic number of digits */
2579 int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2580 int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2581 int size2 = size1 + 1;
2582 int font1 = pos->font;
2583 int font2 = pos->font_alt;
2585 size = (value < value_change ? size1 : size2);
2586 font = (value < value_change ? font1 : font2);
2589 /* clear background if value just changed its size (dynamic digits) */
2590 if ((last_value < value_change) != (value < value_change))
2592 int width1 = size1 * getFontWidth(font1);
2593 int width2 = size2 * getFontWidth(font2);
2594 int max_width = MAX(width1, width2);
2595 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2597 pos->width = max_width;
2599 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2600 max_width, max_height);
2607 /* correct text size if "digits" is zero or less */
2609 size = strlen(int2str(value, size));
2611 /* dynamically correct text alignment */
2612 pos->width = size * getFontWidth(font);
2615 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2616 int2str(value, size), font, mask_mode);
2618 else if (type == TYPE_ELEMENT)
2620 int element, graphic;
2624 int dst_x = PANEL_XPOS(pos);
2625 int dst_y = PANEL_YPOS(pos);
2628 if (value != EL_UNDEFINED && value != EL_EMPTY)
2631 graphic = el2panelimg(value);
2633 // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2636 if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2640 getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2643 width = graphic_info[graphic].width * size / TILESIZE;
2644 height = graphic_info[graphic].height * size / TILESIZE;
2648 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2649 dst_x - src_x, dst_y - src_y);
2650 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2655 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2660 if (value == EL_UNDEFINED || value == EL_EMPTY)
2662 element = (last_value == EL_UNDEFINED ? EL_EMPTY : last_value);
2663 graphic = el2panelimg(element);
2665 src_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
2666 src_x = DOOR_GFX_PAGEX5 + ALIGNED_TEXT_XPOS(pos);
2667 src_y = DOOR_GFX_PAGEY1 + ALIGNED_TEXT_YPOS(pos);
2672 graphic = el2panelimg(value);
2674 getSizedGraphicSource(graphic, frame, size, &src_bitmap, &src_x,&src_y);
2677 width = graphic_info[graphic].width * size / TILESIZE;
2678 height = graphic_info[graphic].height * size / TILESIZE;
2680 BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height, dst_x, dst_y);
2683 else if (type == TYPE_STRING)
2685 boolean active = (value != 0);
2686 char *state_normal = "off";
2687 char *state_active = "on";
2688 char *state = (active ? state_active : state_normal);
2689 char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2690 nr == GAME_PANEL_PLAYER_NAME ? setup.player_name :
2691 nr == GAME_PANEL_LEVEL_NAME ? level.name :
2692 nr == GAME_PANEL_LEVEL_AUTHOR ? level.author : NULL);
2694 if (nr == GAME_PANEL_GRAVITY_STATE)
2696 int font1 = pos->font; /* (used for normal state) */
2697 int font2 = pos->font_alt; /* (used for active state) */
2699 int size1 = strlen(state_normal);
2700 int size2 = strlen(state_active);
2701 int width1 = size1 * getFontWidth(font1);
2702 int width2 = size2 * getFontWidth(font2);
2703 int max_width = MAX(width1, width2);
2704 int max_height = MAX(getFontHeight(font1), getFontHeight(font2));
2706 pos->width = max_width;
2708 /* clear background for values that may have changed its size */
2709 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2710 max_width, max_height);
2713 font = (active ? font2 : font1);
2723 /* don't truncate output if "chars" is zero or less */
2726 /* dynamically correct text alignment */
2727 pos->width = size * getFontWidth(font);
2731 s_cut = getStringCopyN(s, size);
2733 DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2734 s_cut, font, mask_mode);
2740 redraw_mask |= REDRAW_DOOR_1;
2743 game_status = GAME_MODE_PLAYING;
2746 void UpdateAndDisplayGameControlValues()
2748 if (tape.warp_forward)
2751 UpdateGameControlValues();
2752 DisplayGameControlValues();
2755 void DrawGameValue_Emeralds(int value)
2757 struct TextPosInfo *pos = &game.panel.gems;
2759 int font_nr = pos->font;
2761 int font_nr = FONT_TEXT_2;
2763 int font_width = getFontWidth(font_nr);
2764 int chars = pos->size;
2767 return; /* !!! USE NEW STUFF !!! */
2770 if (PANEL_DEACTIVATED(pos))
2773 pos->width = chars * font_width;
2775 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2778 void DrawGameValue_Dynamite(int value)
2780 struct TextPosInfo *pos = &game.panel.inventory_count;
2782 int font_nr = pos->font;
2784 int font_nr = FONT_TEXT_2;
2786 int font_width = getFontWidth(font_nr);
2787 int chars = pos->size;
2790 return; /* !!! USE NEW STUFF !!! */
2793 if (PANEL_DEACTIVATED(pos))
2796 pos->width = chars * font_width;
2798 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2801 void DrawGameValue_Score(int value)
2803 struct TextPosInfo *pos = &game.panel.score;
2805 int font_nr = pos->font;
2807 int font_nr = FONT_TEXT_2;
2809 int font_width = getFontWidth(font_nr);
2810 int chars = pos->size;
2813 return; /* !!! USE NEW STUFF !!! */
2816 if (PANEL_DEACTIVATED(pos))
2819 pos->width = chars * font_width;
2821 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2824 void DrawGameValue_Time(int value)
2826 struct TextPosInfo *pos = &game.panel.time;
2827 static int last_value = -1;
2830 int chars = pos->size;
2832 int font1_nr = pos->font;
2833 int font2_nr = pos->font_alt;
2835 int font1_nr = FONT_TEXT_2;
2836 int font2_nr = FONT_TEXT_1;
2838 int font_nr = font1_nr;
2839 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2842 return; /* !!! USE NEW STUFF !!! */
2845 if (PANEL_DEACTIVATED(pos))
2848 if (use_dynamic_chars) /* use dynamic number of chars */
2850 chars = (value < 1000 ? chars1 : chars2);
2851 font_nr = (value < 1000 ? font1_nr : font2_nr);
2854 /* clear background if value just changed its size (dynamic chars only) */
2855 if (use_dynamic_chars && (last_value < 1000) != (value < 1000))
2857 int width1 = chars1 * getFontWidth(font1_nr);
2858 int width2 = chars2 * getFontWidth(font2_nr);
2859 int max_width = MAX(width1, width2);
2860 int max_height = MAX(getFontHeight(font1_nr), getFontHeight(font2_nr));
2862 pos->width = max_width;
2864 ClearRectangleOnBackground(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2865 max_width, max_height);
2868 pos->width = chars * getFontWidth(font_nr);
2870 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2875 void DrawGameValue_Level(int value)
2877 struct TextPosInfo *pos = &game.panel.level_number;
2880 int chars = pos->size;
2882 int font1_nr = pos->font;
2883 int font2_nr = pos->font_alt;
2885 int font1_nr = FONT_TEXT_2;
2886 int font2_nr = FONT_TEXT_1;
2888 int font_nr = font1_nr;
2889 boolean use_dynamic_chars = (chars == -1 ? TRUE : FALSE);
2892 return; /* !!! USE NEW STUFF !!! */
2895 if (PANEL_DEACTIVATED(pos))
2898 if (use_dynamic_chars) /* use dynamic number of chars */
2900 chars = (level_nr < 100 ? chars1 : chars2);
2901 font_nr = (level_nr < 100 ? font1_nr : font2_nr);
2904 pos->width = chars * getFontWidth(font_nr);
2906 DrawText(PANEL_XPOS(pos), PANEL_YPOS(pos), int2str(value, chars), font_nr);
2909 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
2912 struct TextPosInfo *pos = &game.panel.keys;
2915 int base_key_graphic = EL_KEY_1;
2920 return; /* !!! USE NEW STUFF !!! */
2924 if (PANEL_DEACTIVATED(pos))
2929 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2930 base_key_graphic = EL_EM_KEY_1;
2934 pos->width = 4 * MINI_TILEX;
2938 for (i = 0; i < MAX_NUM_KEYS; i++)
2940 /* currently only 4 of 8 possible keys are displayed */
2941 for (i = 0; i < STD_NUM_KEYS; i++)
2945 struct TextPosInfo *pos = &game.panel.key[i];
2947 int src_x = DOOR_GFX_PAGEX5 + 18 + (i % 4) * MINI_TILEX;
2948 int src_y = DOOR_GFX_PAGEY1 + 123;
2950 int dst_x = PANEL_XPOS(pos);
2951 int dst_y = PANEL_YPOS(pos);
2953 int dst_x = PANEL_XPOS(pos) + i * MINI_TILEX;
2954 int dst_y = PANEL_YPOS(pos);
2958 int element = (i >= STD_NUM_KEYS ? EL_EMC_KEY_5 - 4 :
2959 level.game_engine_type == GAME_ENGINE_TYPE_EM ? EL_EM_KEY_1 :
2961 int graphic = el2edimg(element);
2965 if (PANEL_DEACTIVATED(pos))
2970 /* masked blit with tiles from half-size scaled bitmap does not work yet
2971 (no mask bitmap created for these sizes after loading and scaling) --
2972 solution: load without creating mask, scale, then create final mask */
2974 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2975 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
2980 int graphic = el2edimg(base_key_graphic + i);
2985 getMiniGraphicSource(graphic, &src_bitmap, &src_x, &src_y);
2987 SetClipOrigin(src_bitmap, src_bitmap->stored_clip_gc,
2988 dst_x - src_x, dst_y - src_y);
2989 BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, MINI_TILEX, MINI_TILEY,
2995 DrawMiniGraphicExt(drawto, dst_x, dst_y, graphic);
2997 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
2998 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3001 DrawMiniGraphicExt(drawto, dst_x, dst_y, el2edimg(base_key_graphic + i));
3003 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto, src_x, src_y,
3004 MINI_TILEX, MINI_TILEY, dst_x, dst_y);
3012 void DrawGameValue_Emeralds(int value)
3014 int font_nr = FONT_TEXT_2;
3015 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3017 if (PANEL_DEACTIVATED(game.panel.gems))
3020 DrawText(DX_EMERALDS + xpos, DY_EMERALDS, int2str(value, 3), font_nr);
3023 void DrawGameValue_Dynamite(int value)
3025 int font_nr = FONT_TEXT_2;
3026 int xpos = (3 * 14 - 3 * getFontWidth(font_nr)) / 2;
3028 if (PANEL_DEACTIVATED(game.panel.inventory_count))
3031 DrawText(DX_DYNAMITE + xpos, DY_DYNAMITE, int2str(value, 3), font_nr);
3034 void DrawGameValue_Score(int value)
3036 int font_nr = FONT_TEXT_2;
3037 int xpos = (5 * 14 - 5 * getFontWidth(font_nr)) / 2;
3039 if (PANEL_DEACTIVATED(game.panel.score))
3042 DrawText(DX_SCORE + xpos, DY_SCORE, int2str(value, 5), font_nr);
3045 void DrawGameValue_Time(int value)
3047 int font1_nr = FONT_TEXT_2;
3049 int font2_nr = FONT_TEXT_1;
3051 int font2_nr = FONT_LEVEL_NUMBER;
3053 int xpos3 = (3 * 14 - 3 * getFontWidth(font1_nr)) / 2;
3054 int xpos4 = (4 * 10 - 4 * getFontWidth(font2_nr)) / 2;
3056 if (PANEL_DEACTIVATED(game.panel.time))
3059 /* clear background if value just changed its size */
3060 if (value == 999 || value == 1000)
3061 ClearRectangleOnBackground(drawto, DX_TIME1, DY_TIME, 14 * 3, 14);
3064 DrawText(DX_TIME1 + xpos3, DY_TIME, int2str(value, 3), font1_nr);
3066 DrawText(DX_TIME2 + xpos4, DY_TIME, int2str(value, 4), font2_nr);
3069 void DrawGameValue_Level(int value)
3071 int font1_nr = FONT_TEXT_2;
3073 int font2_nr = FONT_TEXT_1;
3075 int font2_nr = FONT_LEVEL_NUMBER;
3078 if (PANEL_DEACTIVATED(game.panel.level))
3082 DrawText(DX_LEVEL1, DY_LEVEL, int2str(value, 2), font1_nr);
3084 DrawText(DX_LEVEL2, DY_LEVEL, int2str(value, 3), font2_nr);
3087 void DrawGameValue_Keys(int key[MAX_NUM_KEYS])
3089 int base_key_graphic = EL_KEY_1;
3092 if (PANEL_DEACTIVATED(game.panel.keys))
3095 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3096 base_key_graphic = EL_EM_KEY_1;
3098 /* currently only 4 of 8 possible keys are displayed */
3099 for (i = 0; i < STD_NUM_KEYS; i++)
3101 int x = XX_KEYS + i * MINI_TILEX;
3105 DrawMiniGraphicExt(drawto, DX + x,DY + y, el2edimg(base_key_graphic + i));
3107 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
3108 DOOR_GFX_PAGEX5 + x, y, MINI_TILEX, MINI_TILEY, DX + x,DY + y);
3114 void DrawAllGameValues(int emeralds, int dynamite, int score, int time,
3117 int key[MAX_NUM_KEYS];
3120 /* prevent EM engine from updating time/score values parallel to GameWon() */
3121 if (level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3122 local_player->LevelSolved)
3125 for (i = 0; i < MAX_NUM_KEYS; i++)
3126 key[i] = key_bits & (1 << i);
3128 DrawGameValue_Level(level_nr);
3130 DrawGameValue_Emeralds(emeralds);
3131 DrawGameValue_Dynamite(dynamite);
3132 DrawGameValue_Score(score);
3133 DrawGameValue_Time(time);
3135 DrawGameValue_Keys(key);
3138 void UpdateGameDoorValues()
3140 UpdateGameControlValues();
3143 void DrawGameDoorValues()
3145 DisplayGameControlValues();
3148 void DrawGameDoorValues_OLD()
3150 int time_value = (level.time == 0 ? TimePlayed : TimeLeft);
3151 int dynamite_value = 0;
3152 int score_value = (local_player->LevelSolved ? local_player->score_final :
3153 local_player->score);
3154 int gems_value = local_player->gems_still_needed;
3158 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3160 DrawGameDoorValues_EM();
3165 if (game.centered_player_nr == -1)
3167 for (i = 0; i < MAX_PLAYERS; i++)
3169 for (j = 0; j < MAX_NUM_KEYS; j++)
3170 if (stored_player[i].key[j])
3171 key_bits |= (1 << j);
3173 dynamite_value += stored_player[i].inventory_size;
3178 int player_nr = game.centered_player_nr;
3180 for (i = 0; i < MAX_NUM_KEYS; i++)
3181 if (stored_player[player_nr].key[i])
3182 key_bits |= (1 << i);
3184 dynamite_value = stored_player[player_nr].inventory_size;
3187 DrawAllGameValues(gems_value, dynamite_value, score_value, time_value,
3193 =============================================================================
3195 -----------------------------------------------------------------------------
3196 initialize game engine due to level / tape version number
3197 =============================================================================
3200 static void InitGameEngine()
3202 int i, j, k, l, x, y;
3204 /* set game engine from tape file when re-playing, else from level file */
3205 game.engine_version = (tape.playing ? tape.engine_version :
3206 level.game_version);
3208 /* ---------------------------------------------------------------------- */
3209 /* set flags for bugs and changes according to active game engine version */
3210 /* ---------------------------------------------------------------------- */
3213 Summary of bugfix/change:
3214 Fixed handling for custom elements that change when pushed by the player.
3216 Fixed/changed in version:
3220 Before 3.1.0, custom elements that "change when pushing" changed directly
3221 after the player started pushing them (until then handled in "DigField()").
3222 Since 3.1.0, these custom elements are not changed until the "pushing"
3223 move of the element is finished (now handled in "ContinueMoving()").
3225 Affected levels/tapes:
3226 The first condition is generally needed for all levels/tapes before version
3227 3.1.0, which might use the old behaviour before it was changed; known tapes
3228 that are affected are some tapes from the level set "Walpurgis Gardens" by
3230 The second condition is an exception from the above case and is needed for
3231 the special case of tapes recorded with game (not engine!) version 3.1.0 or
3232 above (including some development versions of 3.1.0), but before it was
3233 known that this change would break tapes like the above and was fixed in
3234 3.1.1, so that the changed behaviour was active although the engine version
3235 while recording maybe was before 3.1.0. There is at least one tape that is
3236 affected by this exception, which is the tape for the one-level set "Bug
3237 Machine" by Juergen Bonhagen.
3240 game.use_change_when_pushing_bug =
3241 (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3243 tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3244 tape.game_version < VERSION_IDENT(3,1,1,0)));
3247 Summary of bugfix/change:
3248 Fixed handling for blocking the field the player leaves when moving.
3250 Fixed/changed in version:
3254 Before 3.1.1, when "block last field when moving" was enabled, the field
3255 the player is leaving when moving was blocked for the time of the move,
3256 and was directly unblocked afterwards. This resulted in the last field
3257 being blocked for exactly one less than the number of frames of one player
3258 move. Additionally, even when blocking was disabled, the last field was
3259 blocked for exactly one frame.
3260 Since 3.1.1, due to changes in player movement handling, the last field
3261 is not blocked at all when blocking is disabled. When blocking is enabled,
3262 the last field is blocked for exactly the number of frames of one player
3263 move. Additionally, if the player is Murphy, the hero of Supaplex, the
3264 last field is blocked for exactly one more than the number of frames of
3267 Affected levels/tapes:
3268 (!!! yet to be determined -- probably many !!!)
3271 game.use_block_last_field_bug =
3272 (game.engine_version < VERSION_IDENT(3,1,1,0));
3275 Summary of bugfix/change:
3276 Changed behaviour of CE changes with multiple changes per single frame.
3278 Fixed/changed in version:
3282 Before 3.2.0-6, only one single CE change was allowed in each engine frame.
3283 This resulted in race conditions where CEs seem to behave strange in some
3284 situations (where triggered CE changes were just skipped because there was
3285 already a CE change on that tile in the playfield in that engine frame).
3286 Since 3.2.0-6, this was changed to allow up to MAX_NUM_CHANGES_PER_FRAME.
3287 (The number of changes per frame must be limited in any case, because else
3288 it is easily possible to define CE changes that would result in an infinite
3289 loop, causing the whole game to freeze. The MAX_NUM_CHANGES_PER_FRAME value
3290 should be set large enough so that it would only be reached in cases where
3291 the corresponding CE change conditions run into a loop. Therefore, it seems
3292 to be reasonable to set MAX_NUM_CHANGES_PER_FRAME to the same value as the
3293 maximal number of change pages for custom elements.)
3295 Affected levels/tapes:
3299 #if USE_ONLY_ONE_CHANGE_PER_FRAME
3300 game.max_num_changes_per_frame = 1;
3302 game.max_num_changes_per_frame =
3303 (game.engine_version < VERSION_IDENT(3,2,0,6) ? 1 : 32);
3306 /* ---------------------------------------------------------------------- */
3308 /* default scan direction: scan playfield from top/left to bottom/right */
3309 InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3311 /* dynamically adjust element properties according to game engine version */
3312 InitElementPropertiesEngine(game.engine_version);
3315 printf("level %d: level version == %06d\n", level_nr, level.game_version);
3316 printf(" tape version == %06d [%s] [file: %06d]\n",
3317 tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
3319 printf(" => game.engine_version == %06d\n", game.engine_version);
3322 /* ---------- initialize player's initial move delay --------------------- */
3324 /* dynamically adjust player properties according to level information */
3325 for (i = 0; i < MAX_PLAYERS; i++)
3326 game.initial_move_delay_value[i] =
3327 get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3329 /* dynamically adjust player properties according to game engine version */
3330 for (i = 0; i < MAX_PLAYERS; i++)
3331 game.initial_move_delay[i] =
3332 (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3333 game.initial_move_delay_value[i] : 0);
3335 /* ---------- initialize player's initial push delay --------------------- */
3337 /* dynamically adjust player properties according to game engine version */
3338 game.initial_push_delay_value =
3339 (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3341 /* ---------- initialize changing elements ------------------------------- */
3343 /* initialize changing elements information */
3344 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346 struct ElementInfo *ei = &element_info[i];
3348 /* this pointer might have been changed in the level editor */
3349 ei->change = &ei->change_page[0];
3351 if (!IS_CUSTOM_ELEMENT(i))
3353 ei->change->target_element = EL_EMPTY_SPACE;
3354 ei->change->delay_fixed = 0;
3355 ei->change->delay_random = 0;
3356 ei->change->delay_frames = 1;
3359 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3361 ei->has_change_event[j] = FALSE;
3363 ei->event_page_nr[j] = 0;
3364 ei->event_page[j] = &ei->change_page[0];
3368 /* add changing elements from pre-defined list */
3369 for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3371 struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3372 struct ElementInfo *ei = &element_info[ch_delay->element];
3374 ei->change->target_element = ch_delay->target_element;
3375 ei->change->delay_fixed = ch_delay->change_delay;
3377 ei->change->pre_change_function = ch_delay->pre_change_function;
3378 ei->change->change_function = ch_delay->change_function;
3379 ei->change->post_change_function = ch_delay->post_change_function;
3381 ei->change->can_change = TRUE;
3382 ei->change->can_change_or_has_action = TRUE;
3384 ei->has_change_event[CE_DELAY] = TRUE;
3386 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3387 SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3390 /* ---------- initialize internal run-time variables --------------------- */
3392 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3394 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3396 for (j = 0; j < ei->num_change_pages; j++)
3398 ei->change_page[j].can_change_or_has_action =
3399 (ei->change_page[j].can_change |
3400 ei->change_page[j].has_action);
3404 /* add change events from custom element configuration */
3405 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3407 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3409 for (j = 0; j < ei->num_change_pages; j++)
3411 if (!ei->change_page[j].can_change_or_has_action)
3414 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3416 /* only add event page for the first page found with this event */
3417 if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3419 ei->has_change_event[k] = TRUE;
3421 ei->event_page_nr[k] = j;
3422 ei->event_page[k] = &ei->change_page[j];
3429 /* ---------- initialize reference elements in change conditions --------- */
3431 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3433 int element = EL_CUSTOM_START + i;
3434 struct ElementInfo *ei = &element_info[element];
3436 for (j = 0; j < ei->num_change_pages; j++)
3438 int trigger_element = ei->change_page[j].initial_trigger_element;
3440 if (trigger_element >= EL_PREV_CE_8 &&
3441 trigger_element <= EL_NEXT_CE_8)
3442 trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3444 ei->change_page[j].trigger_element = trigger_element;
3449 /* ---------- initialize run-time trigger player and element ------------- */
3451 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3453 struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3455 for (j = 0; j < ei->num_change_pages; j++)
3457 ei->change_page[j].actual_trigger_element = EL_EMPTY;
3458 ei->change_page[j].actual_trigger_player = EL_EMPTY;
3459 ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3460 ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3461 ei->change_page[j].actual_trigger_ce_value = 0;
3462 ei->change_page[j].actual_trigger_ce_score = 0;
3466 /* ---------- initialize trigger events ---------------------------------- */
3468 /* initialize trigger events information */
3469 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3470 for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3471 trigger_events[i][j] = FALSE;
3473 /* add trigger events from element change event properties */
3474 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3476 struct ElementInfo *ei = &element_info[i];
3478 for (j = 0; j < ei->num_change_pages; j++)
3480 if (!ei->change_page[j].can_change_or_has_action)
3483 if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3485 int trigger_element = ei->change_page[j].trigger_element;
3487 for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3489 if (ei->change_page[j].has_event[k])
3491 if (IS_GROUP_ELEMENT(trigger_element))
3493 struct ElementGroupInfo *group =
3494 element_info[trigger_element].group;
3496 for (l = 0; l < group->num_elements_resolved; l++)
3497 trigger_events[group->element_resolved[l]][k] = TRUE;
3499 else if (trigger_element == EL_ANY_ELEMENT)
3500 for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3501 trigger_events[l][k] = TRUE;
3503 trigger_events[trigger_element][k] = TRUE;
3510 /* ---------- initialize push delay -------------------------------------- */
3512 /* initialize push delay values to default */
3513 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3515 if (!IS_CUSTOM_ELEMENT(i))
3517 /* set default push delay values (corrected since version 3.0.7-1) */
3518 if (game.engine_version < VERSION_IDENT(3,0,7,1))
3520 element_info[i].push_delay_fixed = 2;
3521 element_info[i].push_delay_random = 8;
3525 element_info[i].push_delay_fixed = 8;
3526 element_info[i].push_delay_random = 8;
3531 /* set push delay value for certain elements from pre-defined list */
3532 for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3534 int e = push_delay_list[i].element;
3536 element_info[e].push_delay_fixed = push_delay_list[i].push_delay_fixed;
3537 element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3540 /* set push delay value for Supaplex elements for newer engine versions */
3541 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3543 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3545 if (IS_SP_ELEMENT(i))
3547 /* set SP push delay to just enough to push under a falling zonk */
3548 int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3550 element_info[i].push_delay_fixed = delay;
3551 element_info[i].push_delay_random = 0;
3556 /* ---------- initialize move stepsize ----------------------------------- */
3558 /* initialize move stepsize values to default */
3559 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3560 if (!IS_CUSTOM_ELEMENT(i))
3561 element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3563 /* set move stepsize value for certain elements from pre-defined list */
3564 for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3566 int e = move_stepsize_list[i].element;
3568 element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3571 /* ---------- initialize collect score ----------------------------------- */
3573 /* initialize collect score values for custom elements from initial value */
3574 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3575 if (IS_CUSTOM_ELEMENT(i))
3576 element_info[i].collect_score = element_info[i].collect_score_initial;
3578 /* ---------- initialize collect count ----------------------------------- */
3580 /* initialize collect count values for non-custom elements */
3581 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3582 if (!IS_CUSTOM_ELEMENT(i))
3583 element_info[i].collect_count_initial = 0;
3585 /* add collect count values for all elements from pre-defined list */
3586 for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3587 element_info[collect_count_list[i].element].collect_count_initial =
3588 collect_count_list[i].count;
3590 /* ---------- initialize access direction -------------------------------- */
3592 /* initialize access direction values to default (access from every side) */
3593 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3594 if (!IS_CUSTOM_ELEMENT(i))
3595 element_info[i].access_direction = MV_ALL_DIRECTIONS;
3597 /* set access direction value for certain elements from pre-defined list */
3598 for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3599 element_info[access_direction_list[i].element].access_direction =
3600 access_direction_list[i].direction;
3602 /* ---------- initialize explosion content ------------------------------- */
3603 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3605 if (IS_CUSTOM_ELEMENT(i))
3608 for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3610 /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3612 element_info[i].content.e[x][y] =
3613 (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3614 i == EL_PLAYER_2 ? EL_EMERALD_RED :
3615 i == EL_PLAYER_3 ? EL_EMERALD :
3616 i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3617 i == EL_MOLE ? EL_EMERALD_RED :
3618 i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3619 i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3620 i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3621 i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3622 i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3623 i == EL_WALL_EMERALD ? EL_EMERALD :
3624 i == EL_WALL_DIAMOND ? EL_DIAMOND :
3625 i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3626 i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3627 i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3628 i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3629 i == EL_WALL_PEARL ? EL_PEARL :
3630 i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3635 /* ---------- initialize recursion detection ------------------------------ */
3636 recursion_loop_depth = 0;
3637 recursion_loop_detected = FALSE;
3638 recursion_loop_element = EL_UNDEFINED;
3640 /* ---------- initialize graphics engine ---------------------------------- */
3641 game.scroll_delay_value =
3642 (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3643 setup.scroll_delay ? setup.scroll_delay_value : 0);
3644 game.scroll_delay_value =
3645 MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3648 int get_num_special_action(int element, int action_first, int action_last)
3650 int num_special_action = 0;
3653 for (i = action_first; i <= action_last; i++)
3655 boolean found = FALSE;
3657 for (j = 0; j < NUM_DIRECTIONS; j++)
3658 if (el_act_dir2img(element, i, j) !=
3659 el_act_dir2img(element, ACTION_DEFAULT, j))
3663 num_special_action++;
3668 return num_special_action;
3673 =============================================================================
3675 -----------------------------------------------------------------------------
3676 initialize and start new game
3677 =============================================================================
3682 boolean emulate_bd = TRUE; /* unless non-BOULDERDASH elements found */
3683 boolean emulate_sb = TRUE; /* unless non-SOKOBAN elements found */
3684 boolean emulate_sp = TRUE; /* unless non-SUPAPLEX elements found */
3686 boolean do_fading = (game_status == GAME_MODE_MAIN);
3689 int initial_move_dir = MV_DOWN;
3691 int initial_move_dir = MV_NONE;
3695 game_status = GAME_MODE_PLAYING;
3698 InitGameControlValues();
3700 /* don't play tapes over network */
3701 network_playing = (options.network && !tape.playing);
3703 for (i = 0; i < MAX_PLAYERS; i++)
3705 struct PlayerInfo *player = &stored_player[i];
3707 player->index_nr = i;
3708 player->index_bit = (1 << i);
3709 player->element_nr = EL_PLAYER_1 + i;
3711 player->present = FALSE;
3712 player->active = FALSE;
3713 player->mapped = FALSE;
3715 player->killed = FALSE;
3716 player->reanimated = FALSE;
3719 player->effective_action = 0;
3720 player->programmed_action = 0;
3723 player->score_final = 0;
3725 player->gems_still_needed = level.gems_needed;
3726 player->sokobanfields_still_needed = 0;
3727 player->lights_still_needed = 0;
3728 player->friends_still_needed = 0;
3730 for (j = 0; j < MAX_NUM_KEYS; j++)
3731 player->key[j] = FALSE;
3733 player->num_white_keys = 0;
3735 player->dynabomb_count = 0;
3736 player->dynabomb_size = 1;
3737 player->dynabombs_left = 0;
3738 player->dynabomb_xl = FALSE;
3740 player->MovDir = initial_move_dir;
3743 player->GfxDir = initial_move_dir;
3744 player->GfxAction = ACTION_DEFAULT;
3746 player->StepFrame = 0;
3748 player->initial_element = player->element_nr;
3749 player->artwork_element =
3750 (level.use_artwork_element[i] ? level.artwork_element[i] :
3751 player->element_nr);
3752 player->use_murphy = FALSE;
3754 player->block_last_field = FALSE; /* initialized in InitPlayerField() */
3755 player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3757 player->gravity = level.initial_player_gravity[i];
3759 player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3761 player->actual_frame_counter = 0;
3763 player->step_counter = 0;
3765 player->last_move_dir = initial_move_dir;
3767 player->is_active = FALSE;
3769 player->is_waiting = FALSE;
3770 player->is_moving = FALSE;
3771 player->is_auto_moving = FALSE;
3772 player->is_digging = FALSE;
3773 player->is_snapping = FALSE;
3774 player->is_collecting = FALSE;
3775 player->is_pushing = FALSE;
3776 player->is_switching = FALSE;
3777 player->is_dropping = FALSE;
3778 player->is_dropping_pressed = FALSE;
3780 player->is_bored = FALSE;
3781 player->is_sleeping = FALSE;
3783 player->frame_counter_bored = -1;
3784 player->frame_counter_sleeping = -1;
3786 player->anim_delay_counter = 0;
3787 player->post_delay_counter = 0;
3789 player->dir_waiting = initial_move_dir;
3790 player->action_waiting = ACTION_DEFAULT;
3791 player->last_action_waiting = ACTION_DEFAULT;
3792 player->special_action_bored = ACTION_DEFAULT;
3793 player->special_action_sleeping = ACTION_DEFAULT;
3795 player->switch_x = -1;
3796 player->switch_y = -1;
3798 player->drop_x = -1;
3799 player->drop_y = -1;
3801 player->show_envelope = 0;
3803 SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3805 player->push_delay = -1; /* initialized when pushing starts */
3806 player->push_delay_value = game.initial_push_delay_value;
3808 player->drop_delay = 0;
3809 player->drop_pressed_delay = 0;
3811 player->last_jx = -1;
3812 player->last_jy = -1;
3816 player->shield_normal_time_left = 0;
3817 player->shield_deadly_time_left = 0;
3819 player->inventory_infinite_element = EL_UNDEFINED;
3820 player->inventory_size = 0;
3822 if (level.use_initial_inventory[i])
3824 for (j = 0; j < level.initial_inventory_size[i]; j++)
3826 int element = level.initial_inventory_content[i][j];
3827 int collect_count = element_info[element].collect_count_initial;
3830 if (!IS_CUSTOM_ELEMENT(element))
3833 if (collect_count == 0)
3834 player->inventory_infinite_element = element;
3836 for (k = 0; k < collect_count; k++)
3837 if (player->inventory_size < MAX_INVENTORY_SIZE)
3838 player->inventory_element[player->inventory_size++] = element;
3842 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3843 SnapField(player, 0, 0);
3845 player->LevelSolved = FALSE;
3846 player->GameOver = FALSE;
3848 player->LevelSolved_GameWon = FALSE;
3849 player->LevelSolved_GameEnd = FALSE;
3850 player->LevelSolved_PanelOff = FALSE;
3851 player->LevelSolved_SaveTape = FALSE;
3852 player->LevelSolved_SaveScore = FALSE;
3853 player->LevelSolved_CountingTime = 0;
3854 player->LevelSolved_CountingScore = 0;
3856 map_player_action[i] = i;
3859 network_player_action_received = FALSE;
3861 #if defined(NETWORK_AVALIABLE)
3862 /* initial null action */
3863 if (network_playing)
3864 SendToServer_MovePlayer(MV_NONE);
3873 TimeLeft = level.time;
3876 ScreenMovDir = MV_NONE;
3880 ScrollStepSize = 0; /* will be correctly initialized by ScrollScreen() */
3882 AllPlayersGone = FALSE;
3884 game.yamyam_content_nr = 0;
3885 game.robot_wheel_active = FALSE;
3886 game.magic_wall_active = FALSE;
3887 game.magic_wall_time_left = 0;
3888 game.light_time_left = 0;
3889 game.timegate_time_left = 0;
3890 game.switchgate_pos = 0;
3891 game.wind_direction = level.wind_direction_initial;
3893 #if !USE_PLAYER_GRAVITY
3894 game.gravity = FALSE;
3895 game.explosions_delayed = TRUE;
3898 game.lenses_time_left = 0;
3899 game.magnify_time_left = 0;
3901 game.ball_state = level.ball_state_initial;
3902 game.ball_content_nr = 0;
3904 game.envelope_active = FALSE;
3906 /* set focus to local player for network games, else to all players */
3907 game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3908 game.centered_player_nr_next = game.centered_player_nr;
3909 game.set_centered_player = FALSE;
3911 if (network_playing && tape.recording)
3913 /* store client dependent player focus when recording network games */
3914 tape.centered_player_nr_next = game.centered_player_nr_next;
3915 tape.set_centered_player = TRUE;
3918 for (i = 0; i < NUM_BELTS; i++)
3920 game.belt_dir[i] = MV_NONE;
3921 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
3924 for (i = 0; i < MAX_NUM_AMOEBA; i++)
3925 AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3927 SCAN_PLAYFIELD(x, y)
3929 Feld[x][y] = level.field[x][y];
3930 MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3931 ChangeDelay[x][y] = 0;
3932 ChangePage[x][y] = -1;
3933 #if USE_NEW_CUSTOM_VALUE
3934 CustomValue[x][y] = 0; /* initialized in InitField() */
3936 Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3938 WasJustMoving[x][y] = 0;
3939 WasJustFalling[x][y] = 0;
3940 CheckCollision[x][y] = 0;
3941 CheckImpact[x][y] = 0;
3943 Pushed[x][y] = FALSE;
3945 ChangeCount[x][y] = 0;
3946 ChangeEvent[x][y] = -1;
3948 ExplodePhase[x][y] = 0;
3949 ExplodeDelay[x][y] = 0;
3950 ExplodeField[x][y] = EX_TYPE_NONE;
3952 RunnerVisit[x][y] = 0;
3953 PlayerVisit[x][y] = 0;
3956 GfxRandom[x][y] = INIT_GFX_RANDOM();
3957 GfxElement[x][y] = EL_UNDEFINED;
3958 GfxAction[x][y] = ACTION_DEFAULT;
3959 GfxDir[x][y] = MV_NONE;
3960 GfxRedraw[x][y] = GFX_REDRAW_NONE;
3963 SCAN_PLAYFIELD(x, y)
3965 if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3967 if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3969 if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3972 InitField(x, y, TRUE);
3974 ResetGfxAnimation(x, y);
3979 for (i = 0; i < MAX_PLAYERS; i++)
3981 struct PlayerInfo *player = &stored_player[i];
3983 /* set number of special actions for bored and sleeping animation */
3984 player->num_special_action_bored =
3985 get_num_special_action(player->artwork_element,
3986 ACTION_BORING_1, ACTION_BORING_LAST);
3987 player->num_special_action_sleeping =
3988 get_num_special_action(player->artwork_element,
3989 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3992 game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3993 emulate_sb ? EMU_SOKOBAN :
3994 emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3996 #if USE_NEW_ALL_SLIPPERY
3997 /* initialize type of slippery elements */
3998 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4000 if (!IS_CUSTOM_ELEMENT(i))
4002 /* default: elements slip down either to the left or right randomly */
4003 element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4005 /* SP style elements prefer to slip down on the left side */
4006 if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4007 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4009 /* BD style elements prefer to slip down on the left side */
4010 if (game.emulation == EMU_BOULDERDASH)
4011 element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4016 /* initialize explosion and ignition delay */
4017 for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4019 if (!IS_CUSTOM_ELEMENT(i))
4022 int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4023 game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4024 game.emulation == EMU_SUPAPLEX ? 3 : 2);
4025 int last_phase = (num_phase + 1) * delay;
4026 int half_phase = (num_phase / 2) * delay;
4028 element_info[i].explosion_delay = last_phase - 1;
4029 element_info[i].ignition_delay = half_phase;
4031 if (i == EL_BLACK_ORB)
4032 element_info[i].ignition_delay = 1;
4036 if (element_info[i].explosion_delay < 1) /* !!! check again !!! */
4037 element_info[i].explosion_delay = 1;
4039 if (element_info[i].ignition_delay < 1) /* !!! check again !!! */
4040 element_info[i].ignition_delay = 1;
4044 /* correct non-moving belts to start moving left */
4045 for (i = 0; i < NUM_BELTS; i++)
4046 if (game.belt_dir[i] == MV_NONE)
4047 game.belt_dir_nr[i] = 3; /* not moving, next moving left */
4049 #if USE_NEW_PLAYER_ASSIGNMENTS
4050 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4051 /* choose default local player */
4052 local_player = &stored_player[0];
4054 for (i = 0; i < MAX_PLAYERS; i++)
4055 stored_player[i].connected = FALSE;
4057 local_player->connected = TRUE;
4058 /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
4062 /* try to guess locally connected team mode players (needed for correct
4063 assignment of player figures from level to locally playing players) */
4065 for (i = 0; i < MAX_PLAYERS; i++)
4066 if (tape.player_participates[i])
4067 stored_player[i].connected = TRUE;
4069 else if (setup.team_mode && !options.network)
4071 /* try to guess locally connected team mode players (needed for correct
4072 assignment of player figures from level to locally playing players) */
4074 for (i = 0; i < MAX_PLAYERS; i++)
4075 if (setup.input[i].use_joystick ||
4076 setup.input[i].key.left != KSYM_UNDEFINED)
4077 stored_player[i].connected = TRUE;
4081 for (i = 0; i < MAX_PLAYERS; i++)
4082 printf("::: player %d: %s\n", i,
4083 (stored_player[i].connected ? "connected" : "not connected"));
4085 for (i = 0; i < MAX_PLAYERS; i++)
4086 printf("::: player %d: %s\n", i,
4087 (stored_player[i].present ? "present" : "not present"));
4090 /* check if any connected player was not found in playfield */
4091 for (i = 0; i < MAX_PLAYERS; i++)
4093 struct PlayerInfo *player = &stored_player[i];
4095 if (player->connected && !player->present)
4097 struct PlayerInfo *field_player = NULL;
4100 printf("::: looking for field player for player %d ...\n", i);
4103 /* assign first free player found that is present in the playfield */
4105 /* first try: look for unmapped playfield player that is not connected */
4106 if (field_player == NULL)
4107 for (j = 0; j < MAX_PLAYERS; j++)
4108 if (stored_player[j].present &&
4109 !stored_player[j].mapped &&
4110 !stored_player[j].connected)
4111 field_player = &stored_player[j];
4113 /* second try: look for *any* unmapped playfield player */
4114 if (field_player == NULL)
4115 for (j = 0; j < MAX_PLAYERS; j++)
4116 if (stored_player[j].present &&
4117 !stored_player[j].mapped)
4118 field_player = &stored_player[j];
4120 if (field_player != NULL)
4122 int jx = field_player->jx, jy = field_player->jy;
4125 printf("::: found player figure %d\n", field_player->index_nr);
4128 player->present = FALSE;
4129 player->active = FALSE;
4131 field_player->present = TRUE;
4132 field_player->active = TRUE;
4135 player->initial_element = field_player->initial_element;
4136 player->artwork_element = field_player->artwork_element;
4138 player->block_last_field = field_player->block_last_field;
4139 player->block_delay_adjustment = field_player->block_delay_adjustment;
4142 StorePlayer[jx][jy] = field_player->element_nr;
4144 field_player->jx = field_player->last_jx = jx;
4145 field_player->jy = field_player->last_jy = jy;
4147 if (local_player == player)
4148 local_player = field_player;
4150 map_player_action[field_player->index_nr] = i;
4152 field_player->mapped = TRUE;
4155 printf("::: map_player_action[%d] == %d\n",
4156 field_player->index_nr, i);
4161 if (player->connected && player->present)
4162 player->mapped = TRUE;
4167 /* check if any connected player was not found in playfield */
4168 for (i = 0; i < MAX_PLAYERS; i++)
4170 struct PlayerInfo *player = &stored_player[i];
4172 if (player->connected && !player->present)
4174 for (j = 0; j < MAX_PLAYERS; j++)
4176 struct PlayerInfo *field_player = &stored_player[j];
4177 int jx = field_player->jx, jy = field_player->jy;
4179 /* assign first free player found that is present in the playfield */
4180 if (field_player->present && !field_player->connected)
4182 player->present = TRUE;
4183 player->active = TRUE;
4185 field_player->present = FALSE;
4186 field_player->active = FALSE;
4188 player->initial_element = field_player->initial_element;
4189 player->artwork_element = field_player->artwork_element;
4191 player->block_last_field = field_player->block_last_field;
4192 player->block_delay_adjustment = field_player->block_delay_adjustment;
4194 StorePlayer[jx][jy] = player->element_nr;
4196 player->jx = player->last_jx = jx;
4197 player->jy = player->last_jy = jy;
4207 printf("::: local_player->present == %d\n", local_player->present);
4212 /* when playing a tape, eliminate all players who do not participate */
4214 #if USE_NEW_PLAYER_ASSIGNMENTS
4215 for (i = 0; i < MAX_PLAYERS; i++)
4217 if (stored_player[i].active &&
4218 !tape.player_participates[map_player_action[i]])
4220 struct PlayerInfo *player = &stored_player[i];
4221 int jx = player->jx, jy = player->jy;
4223 player->active = FALSE;
4224 StorePlayer[jx][jy] = 0;
4225 Feld[jx][jy] = EL_EMPTY;
4229 for (i = 0; i < MAX_PLAYERS; i++)
4231 if (stored_player[i].active &&
4232 !tape.player_participates[i])
4234 struct PlayerInfo *player = &stored_player[i];
4235 int jx = player->jx, jy = player->jy;
4237 player->active = FALSE;
4238 StorePlayer[jx][jy] = 0;
4239 Feld[jx][jy] = EL_EMPTY;
4244 else if (!options.network && !setup.team_mode) /* && !tape.playing */
4246 /* when in single player mode, eliminate all but the first active player */
4248 for (i = 0; i < MAX_PLAYERS; i++)
4250 if (stored_player[i].active)
4252 for (j = i + 1; j < MAX_PLAYERS; j++)
4254 if (stored_player[j].active)
4256 struct PlayerInfo *player = &stored_player[j];
4257 int jx = player->jx, jy = player->jy;
4259 player->active = FALSE;
4260 player->present = FALSE;
4262 StorePlayer[jx][jy] = 0;
4263 Feld[jx][jy] = EL_EMPTY;
4270 /* when recording the game, store which players take part in the game */
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274 for (i = 0; i < MAX_PLAYERS; i++)
4275 if (stored_player[i].connected)
4276 tape.player_participates[i] = TRUE;
4278 for (i = 0; i < MAX_PLAYERS; i++)
4279 if (stored_player[i].active)
4280 tape.player_participates[i] = TRUE;
4286 for (i = 0; i < MAX_PLAYERS; i++)
4288 struct PlayerInfo *player = &stored_player[i];
4290 printf("Player %d: present == %d, connected == %d, active == %d.\n",
4295 if (local_player == player)
4296 printf("Player %d is local player.\n", i+1);
4300 if (BorderElement == EL_EMPTY)
4303 SBX_Right = lev_fieldx - SCR_FIELDX;
4305 SBY_Lower = lev_fieldy - SCR_FIELDY;
4310 SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4312 SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4315 if (lev_fieldx + (SBX_Left == -1 ? 2 : 0) <= SCR_FIELDX)
4316 SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4318 if (lev_fieldy + (SBY_Upper == -1 ? 2 : 0) <= SCR_FIELDY)
4319 SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4321 /* if local player not found, look for custom element that might create
4322 the player (make some assumptions about the right custom element) */
4323 if (!local_player->present)
4325 int start_x = 0, start_y = 0;
4326 int found_rating = 0;
4327 int found_element = EL_UNDEFINED;
4328 int player_nr = local_player->index_nr;
4330 SCAN_PLAYFIELD(x, y)
4332 int element = Feld[x][y];
4337 if (level.use_start_element[player_nr] &&
4338 level.start_element[player_nr] == element &&
4345 found_element = element;
4348 if (!IS_CUSTOM_ELEMENT(element))
4351 if (CAN_CHANGE(element))
4353 for (i = 0; i < element_info[element].num_change_pages; i++)
4355 /* check for player created from custom element as single target */
4356 content = element_info[element].change_page[i].target_element;
4357 is_player = ELEM_IS_PLAYER(content);
4359 if (is_player && (found_rating < 3 ||
4360 (found_rating == 3 && element < found_element)))
4366 found_element = element;
4371 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4373 /* check for player created from custom element as explosion content */
4374 content = element_info[element].content.e[xx][yy];
4375 is_player = ELEM_IS_PLAYER(content);
4377 if (is_player && (found_rating < 2 ||
4378 (found_rating == 2 && element < found_element)))
4380 start_x = x + xx - 1;
4381 start_y = y + yy - 1;
4384 found_element = element;
4387 if (!CAN_CHANGE(element))
4390 for (i = 0; i < element_info[element].num_change_pages; i++)
4392 /* check for player created from custom element as extended target */
4394 element_info[element].change_page[i].target_content.e[xx][yy];
4396 is_player = ELEM_IS_PLAYER(content);
4398 if (is_player && (found_rating < 1 ||
4399 (found_rating == 1 && element < found_element)))
4401 start_x = x + xx - 1;
4402 start_y = y + yy - 1;
4405 found_element = element;
4411 scroll_x = (start_x < SBX_Left + MIDPOSX ? SBX_Left :
4412 start_x > SBX_Right + MIDPOSX ? SBX_Right :
4415 scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4416 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4421 scroll_x = (local_player->jx < SBX_Left + MIDPOSX ? SBX_Left :
4422 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
4423 local_player->jx - MIDPOSX);
4425 scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
4426 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
4427 local_player->jy - MIDPOSY);
4431 /* do not use PLAYING mask for fading out from main screen */
4432 game_status = GAME_MODE_MAIN;
4437 if (!game.restart_level)
4438 CloseDoor(DOOR_CLOSE_1);
4441 if (level_editor_test_game)
4442 FadeSkipNextFadeIn();
4444 FadeSetEnterScreen();
4446 if (level_editor_test_game)
4447 fading = fading_none;
4449 fading = menu.destination;
4453 FadeOut(REDRAW_FIELD);
4456 FadeOut(REDRAW_FIELD);
4460 game_status = GAME_MODE_PLAYING;
4463 /* !!! FIX THIS (START) !!! */
4464 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4466 InitGameEngine_EM();
4468 /* blit playfield from scroll buffer to normal back buffer for fading in */
4469 BlitScreenToBitmap_EM(backbuffer);
4476 /* after drawing the level, correct some elements */
4477 if (game.timegate_time_left == 0)
4478 CloseAllOpenTimegates();
4480 /* blit playfield from scroll buffer to normal back buffer for fading in */
4481 if (setup.soft_scrolling)
4482 BlitBitmap(fieldbuffer, backbuffer, FX, FY, SXSIZE, SYSIZE, SX, SY);
4484 redraw_mask |= REDRAW_FROM_BACKBUFFER;
4486 /* !!! FIX THIS (END) !!! */
4489 FadeIn(REDRAW_FIELD);
4492 FadeIn(REDRAW_FIELD);
4497 if (!game.restart_level)
4499 /* copy default game door content to main double buffer */
4500 BlitBitmap(graphic_info[IMG_GLOBAL_DOOR].bitmap, drawto,
4501 DOOR_GFX_PAGEX5, DOOR_GFX_PAGEY1, DXSIZE, DYSIZE, DX, DY);
4504 SetPanelBackground();
4505 SetDrawBackgroundMask(REDRAW_DOOR_1);
4508 UpdateAndDisplayGameControlValues();
4510 UpdateGameDoorValues();
4511 DrawGameDoorValues();
4514 if (!game.restart_level)
4518 game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
4519 game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
4520 game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
4524 /* copy actual game door content to door double buffer for OpenDoor() */
4525 BlitBitmap(drawto, bitmap_db_door,
4526 DX, DY, DXSIZE, DYSIZE, DOOR_GFX_PAGEX1, DOOR_GFX_PAGEY1);
4528 OpenDoor(DOOR_OPEN_ALL);
4530 PlaySound(SND_GAME_STARTING);
4532 if (setup.sound_music)
4535 KeyboardAutoRepeatOffUnlessAutoplay();
4539 for (i = 0; i < MAX_PLAYERS; i++)
4540 printf("Player %d %sactive.\n",
4541 i + 1, (stored_player[i].active ? "" : "not "));
4552 game.restart_level = FALSE;
4555 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4557 /* this is used for non-R'n'D game engines to update certain engine values */
4559 /* needed to determine if sounds are played within the visible screen area */
4560 scroll_x = actual_scroll_x;
4561 scroll_y = actual_scroll_y;
4564 void InitMovDir(int x, int y)
4566 int i, element = Feld[x][y];
4567 static int xy[4][2] =
4574 static int direction[3][4] =
4576 { MV_RIGHT, MV_UP, MV_LEFT, MV_DOWN },
4577 { MV_LEFT, MV_DOWN, MV_RIGHT, MV_UP },
4578 { MV_LEFT, MV_RIGHT, MV_UP, MV_DOWN }
4587 Feld[x][y] = EL_BUG;
4588 MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4591 case EL_SPACESHIP_RIGHT:
4592 case EL_SPACESHIP_UP:
4593 case EL_SPACESHIP_LEFT:
4594 case EL_SPACESHIP_DOWN:
4595 Feld[x][y] = EL_SPACESHIP;
4596 MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4599 case EL_BD_BUTTERFLY_RIGHT:
4600 case EL_BD_BUTTERFLY_UP:
4601 case EL_BD_BUTTERFLY_LEFT:
4602 case EL_BD_BUTTERFLY_DOWN:
4603 Feld[x][y] = EL_BD_BUTTERFLY;
4604 MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4607 case EL_BD_FIREFLY_RIGHT:
4608 case EL_BD_FIREFLY_UP:
4609 case EL_BD_FIREFLY_LEFT:
4610 case EL_BD_FIREFLY_DOWN:
4611 Feld[x][y] = EL_BD_FIREFLY;
4612 MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4615 case EL_PACMAN_RIGHT:
4617 case EL_PACMAN_LEFT:
4618 case EL_PACMAN_DOWN:
4619 Feld[x][y] = EL_PACMAN;
4620 MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4623 case EL_YAMYAM_LEFT:
4624 case EL_YAMYAM_RIGHT:
4626 case EL_YAMYAM_DOWN:
4627 Feld[x][y] = EL_YAMYAM;
4628 MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4631 case EL_SP_SNIKSNAK:
4632 MovDir[x][y] = MV_UP;
4635 case EL_SP_ELECTRON:
4636 MovDir[x][y] = MV_LEFT;
4643 Feld[x][y] = EL_MOLE;
4644 MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4648 if (IS_CUSTOM_ELEMENT(element))
4650 struct ElementInfo *ei = &element_info[element];
4651 int move_direction_initial = ei->move_direction_initial;
4652 int move_pattern = ei->move_pattern;
4654 if (move_direction_initial == MV_START_PREVIOUS)
4656 if (MovDir[x][y] != MV_NONE)
4659 move_direction_initial = MV_START_AUTOMATIC;
4662 if (move_direction_initial == MV_START_RANDOM)
4663 MovDir[x][y] = 1 << RND(4);
4664 else if (move_direction_initial & MV_ANY_DIRECTION)
4665 MovDir[x][y] = move_direction_initial;
4666 else if (move_pattern == MV_ALL_DIRECTIONS ||
4667 move_pattern == MV_TURNING_LEFT ||
4668 move_pattern == MV_TURNING_RIGHT ||
4669 move_pattern == MV_TURNING_LEFT_RIGHT ||
4670 move_pattern == MV_TURNING_RIGHT_LEFT ||
4671 move_pattern == MV_TURNING_RANDOM)
4672 MovDir[x][y] = 1 << RND(4);
4673 else if (move_pattern == MV_HORIZONTAL)
4674 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4675 else if (move_pattern == MV_VERTICAL)
4676 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4677 else if (move_pattern & MV_ANY_DIRECTION)
4678 MovDir[x][y] = element_info[element].move_pattern;
4679 else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4680 move_pattern == MV_ALONG_RIGHT_SIDE)
4682 /* use random direction as default start direction */
4683 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4684 MovDir[x][y] = 1 << RND(4);
4686 for (i = 0; i < NUM_DIRECTIONS; i++)
4688 int x1 = x + xy[i][0];
4689 int y1 = y + xy[i][1];
4691 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4693 if (move_pattern == MV_ALONG_RIGHT_SIDE)
4694 MovDir[x][y] = direction[0][i];
4696 MovDir[x][y] = direction[1][i];
4705 MovDir[x][y] = 1 << RND(4);
4707 if (element != EL_BUG &&
4708 element != EL_SPACESHIP &&
4709 element != EL_BD_BUTTERFLY &&
4710 element != EL_BD_FIREFLY)
4713 for (i = 0; i < NUM_DIRECTIONS; i++)
4715 int x1 = x + xy[i][0];
4716 int y1 = y + xy[i][1];
4718 if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4720 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4722 MovDir[x][y] = direction[0][i];
4725 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4726 element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4728 MovDir[x][y] = direction[1][i];
4737 GfxDir[x][y] = MovDir[x][y];
4740 void InitAmoebaNr(int x, int y)
4743 int group_nr = AmoebeNachbarNr(x, y);
4747 for (i = 1; i < MAX_NUM_AMOEBA; i++)
4749 if (AmoebaCnt[i] == 0)
4757 AmoebaNr[x][y] = group_nr;
4758 AmoebaCnt[group_nr]++;
4759 AmoebaCnt2[group_nr]++;
4762 static void PlayerWins(struct PlayerInfo *player)
4764 player->LevelSolved = TRUE;
4765 player->GameOver = TRUE;
4767 player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4768 level.native_em_level->lev->score : player->score);
4770 player->LevelSolved_CountingTime = (level.time == 0 ? TimePlayed : TimeLeft);
4771 player->LevelSolved_CountingScore = player->score_final;
4776 static int time, time_final;
4777 static int score, score_final;
4778 static int game_over_delay_1 = 0;
4779 static int game_over_delay_2 = 0;
4780 int game_over_delay_value_1 = 50;
4781 int game_over_delay_value_2 = 50;
4783 if (!local_player->LevelSolved_GameWon)
4787 /* do not start end game actions before the player stops moving (to exit) */
4788 if (local_player->MovPos)
4791 local_player->LevelSolved_GameWon = TRUE;
4792 local_player->LevelSolved_SaveTape = tape.recording;
4793 local_player->LevelSolved_SaveScore = !tape.playing;
4795 if (tape.auto_play) /* tape might already be stopped here */
4796 tape.auto_play_level_solved = TRUE;
4802 game_over_delay_1 = game_over_delay_value_1;
4803 game_over_delay_2 = game_over_delay_value_2;
4805 time = time_final = (level.time == 0 ? TimePlayed : TimeLeft);
4806 score = score_final = local_player->score_final;
4811 score_final += TimeLeft * level.score[SC_TIME_BONUS];
4813 else if (level.time == 0 && TimePlayed < 999)
4816 score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4819 local_player->score_final = score_final;
4821 if (level_editor_test_game)
4824 score = score_final;
4827 local_player->LevelSolved_CountingTime = time;
4828 local_player->LevelSolved_CountingScore = score;
4830 game_panel_controls[GAME_PANEL_TIME].value = time;
4831 game_panel_controls[GAME_PANEL_SCORE].value = score;
4833 DisplayGameControlValues();
4835 DrawGameValue_Time(time);
4836 DrawGameValue_Score(score);
4840 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4842 if (ExitX >= 0 && ExitY >= 0) /* local player has left the level */
4844 /* close exit door after last player */
4845 if ((AllPlayersGone &&
4846 (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4847 Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4848 Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4849 Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4850 Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4852 int element = Feld[ExitX][ExitY];
4855 if (element == EL_EM_EXIT_OPEN ||
4856 element == EL_EM_STEEL_EXIT_OPEN)
4863 Feld[ExitX][ExitY] =
4864 (element == EL_EXIT_OPEN ? EL_EXIT_CLOSING :
4865 element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4866 element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4867 element == EL_STEEL_EXIT_OPEN ? EL_STEEL_EXIT_CLOSING:
4868 EL_EM_STEEL_EXIT_CLOSING);
4870 PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4874 /* player disappears */
4875 DrawLevelField(ExitX, ExitY);
4878 for (i = 0; i < MAX_PLAYERS; i++)
4880 struct PlayerInfo *player = &stored_player[i];
4882 if (player->present)
4884 RemovePlayer(player);
4886 /* player disappears */
4887 DrawLevelField(player->jx, player->jy);
4892 PlaySound(SND_GAME_WINNING);
4895 if (game_over_delay_1 > 0)
4897 game_over_delay_1--;
4902 if (time != time_final)
4904 int time_to_go = ABS(time_final - time);
4905 int time_count_dir = (time < time_final ? +1 : -1);
4906 int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4908 time += time_count_steps * time_count_dir;
4909 score += time_count_steps * level.score[SC_TIME_BONUS];
4912 local_player->LevelSolved_CountingTime = time;
4913 local_player->LevelSolved_CountingScore = score;
4915 game_panel_controls[GAME_PANEL_TIME].value = time;
4916 game_panel_controls[GAME_PANEL_SCORE].value = score;
4918 DisplayGameControlValues();
4920 DrawGameValue_Time(time);
4921 DrawGameValue_Score(score);
4924 if (time == time_final)
4925 StopSound(SND_GAME_LEVELTIME_BONUS);
4926 else if (setup.sound_loops)
4927 PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4929 PlaySound(SND_GAME_LEVELTIME_BONUS);
4934 local_player->LevelSolved_PanelOff = TRUE;
4936 if (game_over_delay_2 > 0)
4938 game_over_delay_2--;
4951 boolean raise_level = FALSE;
4953 local_player->LevelSolved_GameEnd = TRUE;
4955 CloseDoor(DOOR_CLOSE_1);
4957 if (local_player->LevelSolved_SaveTape)
4964 SaveTapeChecked(tape.level_nr); /* ask to save tape */
4966 SaveTape(tape.level_nr); /* ask to save tape */
4970 if (level_editor_test_game)
4972 game_status = GAME_MODE_MAIN;
4975 DrawAndFadeInMainMenu(REDRAW_FIELD);
4983 if (!local_player->LevelSolved_SaveScore)
4986 FadeOut(REDRAW_FIELD);
4989 game_status = GAME_MODE_MAIN;
4991 DrawAndFadeInMainMenu(REDRAW_FIELD);
4996 if (level_nr == leveldir_current->handicap_level)
4998 leveldir_current->handicap_level++;
4999 SaveLevelSetup_SeriesInfo();
5002 if (level_nr < leveldir_current->last_level)
5003 raise_level = TRUE; /* advance to next level */
5005 if ((hi_pos = NewHiScore()) >= 0)
5007 game_status = GAME_MODE_SCORES;
5009 DrawHallOfFame(hi_pos);
5020 FadeOut(REDRAW_FIELD);
5023 game_status = GAME_MODE_MAIN;
5031 DrawAndFadeInMainMenu(REDRAW_FIELD);
5040 LoadScore(level_nr);
5042 if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5043 local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5046 for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5048 if (local_player->score_final > highscore[k].Score)
5050 /* player has made it to the hall of fame */
5052 if (k < MAX_SCORE_ENTRIES - 1)
5054 int m = MAX_SCORE_ENTRIES - 1;
5057 for (l = k; l < MAX_SCORE_ENTRIES; l++)
5058 if (strEqual(setup.player_name, highscore[l].Name))
5060 if (m == k) /* player's new highscore overwrites his old one */
5064 for (l = m; l > k; l--)
5066 strcpy(highscore[l].Name, highscore[l - 1].Name);
5067 highscore[l].Score = highscore[l - 1].Score;
5074 strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5075 highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5076 highscore[k].Score = local_player->score_final;
5082 else if (!strncmp(setup.player_name, highscore[k].Name,
5083 MAX_PLAYER_NAME_LEN))
5084 break; /* player already there with a higher score */
5090 SaveScore(level_nr);
5095 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
5097 int element = Feld[x][y];
5098 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5099 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5100 int horiz_move = (dx != 0);
5101 int sign = (horiz_move ? dx : dy);
5102 int step = sign * element_info[element].move_stepsize;
5104 /* special values for move stepsize for spring and things on conveyor belt */
5107 if (CAN_FALL(element) &&
5108 y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
5109 step = sign * MOVE_STEPSIZE_NORMAL / 2;
5110 else if (element == EL_SPRING)
5111 step = sign * MOVE_STEPSIZE_NORMAL * 2;
5117 inline static int getElementMoveStepsize(int x, int y)
5119 return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5122 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5124 if (player->GfxAction != action || player->GfxDir != dir)
5127 printf("Player frame reset! (%d => %d, %d => %d)\n",
5128 player->GfxAction, action, player->GfxDir, dir);
5131 player->GfxAction = action;
5132 player->GfxDir = dir;
5134 player->StepFrame = 0;
5138 #if USE_GFX_RESET_GFX_ANIMATION
5139 static void ResetGfxFrame(int x, int y, boolean redraw)
5141 int element = Feld[x][y];
5142 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5143 int last_gfx_frame = GfxFrame[x][y];
5145 if (graphic_info[graphic].anim_global_sync)
5146 GfxFrame[x][y] = FrameCounter;
5147 else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5148 GfxFrame[x][y] = CustomValue[x][y];
5149 else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5150 GfxFrame[x][y] = element_info[element].collect_score;
5151 else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5152 GfxFrame[x][y] = ChangeDelay[x][y];
5154 if (redraw && GfxFrame[x][y] != last_gfx_frame)
5155 DrawLevelGraphicAnimation(x, y, graphic);
5159 static void ResetGfxAnimation(int x, int y)
5161 GfxAction[x][y] = ACTION_DEFAULT;
5162 GfxDir[x][y] = MovDir[x][y];
5165 #if USE_GFX_RESET_GFX_ANIMATION
5166 ResetGfxFrame(x, y, FALSE);
5170 static void ResetRandomAnimationValue(int x, int y)
5172 GfxRandom[x][y] = INIT_GFX_RANDOM();
5175 void InitMovingField(int x, int y, int direction)
5177 int element = Feld[x][y];
5178 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5179 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
5182 boolean is_moving_before, is_moving_after;
5184 boolean continues_moving = (WasJustMoving[x][y] && direction == MovDir[x][y]);
5187 /* check if element was/is moving or being moved before/after mode change */
5190 is_moving_before = (WasJustMoving[x][y] != 0);
5192 /* (!!! this does not work -- WasJustMoving is NOT a boolean value !!!) */
5193 is_moving_before = WasJustMoving[x][y];
5196 is_moving_before = (getElementMoveStepsizeExt(x, y, MovDir[x][y]) != 0);
5198 is_moving_after = (getElementMoveStepsizeExt(x, y, direction) != 0);
5200 /* reset animation only for moving elements which change direction of moving
5201 or which just started or stopped moving
5202 (else CEs with property "can move" / "not moving" are reset each frame) */
5203 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5205 if (is_moving_before != is_moving_after ||
5206 direction != MovDir[x][y])
5207 ResetGfxAnimation(x, y);
5209 if ((is_moving_before || is_moving_after) && !continues_moving)
5210 ResetGfxAnimation(x, y);
5213 if (!continues_moving)
5214 ResetGfxAnimation(x, y);
5217 MovDir[x][y] = direction;
5218 GfxDir[x][y] = direction;
5220 #if USE_GFX_RESET_ONLY_WHEN_MOVING
5221 GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5222 direction == MV_DOWN && CAN_FALL(element) ?
5223 ACTION_FALLING : ACTION_MOVING);
5225 GfxAction[x][y] = (direction == MV_DOWN && CAN_FALL(element) ?
5226 ACTION_FALLING : ACTION_MOVING);
5229 /* this is needed for CEs with property "can move" / "not moving" */
5231 if (is_moving_after)
5233 if (Feld[newx][newy] == EL_EMPTY)
5234 Feld[newx][newy] = EL_BLOCKED;
5236 MovDir[newx][newy] = MovDir[x][y];
5238 #if USE_NEW_CUSTOM_VALUE
5239 CustomValue[newx][newy] = CustomValue[x][y];
5242 GfxFrame[newx][newy] = GfxFrame[x][y];
5243 GfxRandom[newx][newy] = GfxRandom[x][y];
5244 GfxAction[newx][newy] = GfxAction[x][y];
5245 GfxDir[newx][newy] = GfxDir[x][y];
5249 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5251 int direction = MovDir[x][y];
5252 int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5253 int newy = y + (direction & MV_UP ? -1 : direction & MV_DOWN ? +1 : 0);
5259 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5261 int oldx = x, oldy = y;
5262 int direction = MovDir[x][y];
5264 if (direction == MV_LEFT)
5266 else if (direction == MV_RIGHT)
5268 else if (direction == MV_UP)
5270 else if (direction == MV_DOWN)
5273 *comes_from_x = oldx;
5274 *comes_from_y = oldy;
5277 int MovingOrBlocked2Element(int x, int y)
5279 int element = Feld[x][y];
5281 if (element == EL_BLOCKED)
5285 Blocked2Moving(x, y, &oldx, &oldy);
5286 return Feld[oldx][oldy];
5292 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5294 /* like MovingOrBlocked2Element(), but if element is moving
5295 and (x,y) is the field the moving element is just leaving,
5296 return EL_BLOCKED instead of the element value */
5297 int element = Feld[x][y];
5299 if (IS_MOVING(x, y))
5301 if (element == EL_BLOCKED)
5305 Blocked2Moving(x, y, &oldx, &oldy);
5306 return Feld[oldx][oldy];
5315 static void RemoveField(int x, int y)
5317 Feld[x][y] = EL_EMPTY;
5323 #if USE_NEW_CUSTOM_VALUE
5324 CustomValue[x][y] = 0;
5328 ChangeDelay[x][y] = 0;
5329 ChangePage[x][y] = -1;
5330 Pushed[x][y] = FALSE;
5333 ExplodeField[x][y] = EX_TYPE_NONE;
5336 GfxElement[x][y] = EL_UNDEFINED;
5337 GfxAction[x][y] = ACTION_DEFAULT;
5338 GfxDir[x][y] = MV_NONE;
5340 /* !!! this would prevent the removed tile from being redrawn !!! */
5341 GfxRedraw[x][y] = GFX_REDRAW_NONE;
5345 void RemoveMovingField(int x, int y)
5347 int oldx = x, oldy = y, newx = x, newy = y;
5348 int element = Feld[x][y];
5349 int next_element = EL_UNDEFINED;
5351 if (element != EL_BLOCKED && !IS_MOVING(x, y))
5354 if (IS_MOVING(x, y))
5356 Moving2Blocked(x, y, &newx, &newy);
5358 if (Feld[newx][newy] != EL_BLOCKED)
5360 /* element is moving, but target field is not free (blocked), but
5361 already occupied by something different (example: acid pool);
5362 in this case, only remove the moving field, but not the target */
5364 RemoveField(oldx, oldy);
5366 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5368 TEST_DrawLevelField(oldx, oldy);
5373 else if (element == EL_BLOCKED)
5375 Blocked2Moving(x, y, &oldx, &oldy);
5376 if (!IS_MOVING(oldx, oldy))
5380 if (element == EL_BLOCKED &&
5381 (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5382 Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5383 Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5384 Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5385 Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5386 Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
5387 next_element = get_next_element(Feld[oldx][oldy]);
5389 RemoveField(oldx, oldy);
5390 RemoveField(newx, newy);
5392 Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5394 if (next_element != EL_UNDEFINED)
5395 Feld[oldx][oldy] = next_element;
5397 TEST_DrawLevelField(oldx, oldy);
5398 TEST_DrawLevelField(newx, newy);
5401 void DrawDynamite(int x, int y)
5403 int sx = SCREENX(x), sy = SCREENY(y);
5404 int graphic = el2img(Feld[x][y]);
5407 if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5410 if (IS_WALKABLE_INSIDE(Back[x][y]))
5414 DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
5415 else if (Store[x][y])
5416 DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
5418 frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5420 if (Back[x][y] || Store[x][y])
5421 DrawGraphicThruMask(sx, sy, graphic, frame);
5423 DrawGraphic(sx, sy, graphic, frame);
5426 void CheckDynamite(int x, int y)
5428 if (MovDelay[x][y] != 0) /* dynamite is still waiting to explode */
5432 if (MovDelay[x][y] != 0)
5435 PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5441 StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5446 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5448 boolean num_checked_players = 0;
5451 for (i = 0; i < MAX_PLAYERS; i++)
5453 if (stored_player[i].active)
5455 int sx = stored_player[i].jx;
5456 int sy = stored_player[i].jy;
5458 if (num_checked_players == 0)
5465 *sx1 = MIN(*sx1, sx);
5466 *sy1 = MIN(*sy1, sy);
5467 *sx2 = MAX(*sx2, sx);
5468 *sy2 = MAX(*sy2, sy);
5471 num_checked_players++;
5476 static boolean checkIfAllPlayersFitToScreen_RND()
5478 int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5480 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5482 return (sx2 - sx1 < SCR_FIELDX &&
5483 sy2 - sy1 < SCR_FIELDY);
5486 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5488 int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5490 setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5492 *sx = (sx1 + sx2) / 2;
5493 *sy = (sy1 + sy2) / 2;
5496 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5497 boolean center_screen, boolean quick_relocation)
5499 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5500 boolean no_delay = (tape.warp_forward);
5501 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5502 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5504 if (quick_relocation)
5506 if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
5508 if (!level.shifted_relocation || center_screen)
5510 /* quick relocation (without scrolling), with centering of screen */
5512 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5513 x > SBX_Right + MIDPOSX ? SBX_Right :
5516 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5517 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5522 /* quick relocation (without scrolling), but do not center screen */
5524 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5525 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5528 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5529 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5532 int offset_x = x + (scroll_x - center_scroll_x);
5533 int offset_y = y + (scroll_y - center_scroll_y);
5535 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5536 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5537 offset_x - MIDPOSX);
5539 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5540 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5541 offset_y - MIDPOSY);
5547 if (!level.shifted_relocation || center_screen)
5549 /* quick relocation (without scrolling), with centering of screen */
5551 scroll_x = (x < SBX_Left + MIDPOSX ? SBX_Left :
5552 x > SBX_Right + MIDPOSX ? SBX_Right :
5555 scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5556 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5561 /* quick relocation (without scrolling), but do not center screen */
5563 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5564 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5567 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5568 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5571 int offset_x = x + (scroll_x - center_scroll_x);
5572 int offset_y = y + (scroll_y - center_scroll_y);
5574 scroll_x = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5575 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5576 offset_x - MIDPOSX);
5578 scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5579 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5580 offset_y - MIDPOSY);
5583 /* quick relocation (without scrolling), inside visible screen area */
5585 int offset = game.scroll_delay_value;
5587 if ((move_dir == MV_LEFT && scroll_x > x - MIDPOSX + offset) ||
5588 (move_dir == MV_RIGHT && scroll_x < x - MIDPOSX - offset))
5589 scroll_x = x - MIDPOSX + (scroll_x < x - MIDPOSX ? -offset : +offset);
5591 if ((move_dir == MV_UP && scroll_y > y - MIDPOSY + offset) ||
5592 (move_dir == MV_DOWN && scroll_y < y - MIDPOSY - offset))
5593 scroll_y = y - MIDPOSY + (scroll_y < y - MIDPOSY ? -offset : +offset);
5595 /* don't scroll over playfield boundaries */
5596 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
5597 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
5599 /* don't scroll over playfield boundaries */
5600 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
5601 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
5605 RedrawPlayfield(TRUE, 0,0,0,0);
5610 int scroll_xx, scroll_yy;
5612 if (!level.shifted_relocation || center_screen)
5614 /* visible relocation (with scrolling), with centering of screen */
5616 scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5617 x > SBX_Right + MIDPOSX ? SBX_Right :
5620 scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5621 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5626 /* visible relocation (with scrolling), but do not center screen */
5628 int center_scroll_x = (old_x < SBX_Left + MIDPOSX ? SBX_Left :
5629 old_x > SBX_Right + MIDPOSX ? SBX_Right :
5632 int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5633 old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5636 int offset_x = x + (scroll_x - center_scroll_x);
5637 int offset_y = y + (scroll_y - center_scroll_y);
5639 scroll_xx = (offset_x < SBX_Left + MIDPOSX ? SBX_Left :
5640 offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5641 offset_x - MIDPOSX);
5643 scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5644 offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5645 offset_y - MIDPOSY);
5650 /* visible relocation (with scrolling), with centering of screen */
5652 int scroll_xx = (x < SBX_Left + MIDPOSX ? SBX_Left :
5653 x > SBX_Right + MIDPOSX ? SBX_Right :
5656 int scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
5657 y > SBY_Lower + MIDPOSY ? SBY_Lower :
5661 ScrollScreen(NULL, SCROLL_GO_ON); /* scroll last frame to full tile */
5663 while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5666 int fx = FX, fy = FY;
5668 dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5669 dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5671 if (dx == 0 && dy == 0) /* no scrolling needed at all */
5677 fx += dx * TILEX / 2;
5678 fy += dy * TILEY / 2;
5680 ScrollLevel(dx, dy);
5683 /* scroll in two steps of half tile size to make things smoother */
5684 BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5686 Delay(wait_delay_value);
5688 /* scroll second step to align at full tile size */
5690 Delay(wait_delay_value);
5695 Delay(wait_delay_value);
5699 void RelocatePlayer(int jx, int jy, int el_player_raw)
5701 int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5702 int player_nr = GET_PLAYER_NR(el_player);
5703 struct PlayerInfo *player = &stored_player[player_nr];
5704 boolean ffwd_delay = (tape.playing && tape.fast_forward);
5705 boolean no_delay = (tape.warp_forward);
5706 int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5707 int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5708 int old_jx = player->jx;
5709 int old_jy = player->jy;
5710 int old_element = Feld[old_jx][old_jy];
5711 int element = Feld[jx][jy];
5712 boolean player_relocated = (old_jx != jx || old_jy != jy);
5714 int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5715 int move_dir_vert = (jy < old_jy ? MV_UP : jy > old_jy ? MV_DOWN : 0);
5716 int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5717 int enter_side_vert = MV_DIR_OPPOSITE(move_dir_vert);
5718 int leave_side_horiz = move_dir_horiz;
5719 int leave_side_vert = move_dir_vert;
5720 int enter_side = enter_side_horiz | enter_side_vert;
5721 int leave_side = leave_side_horiz | leave_side_vert;
5723 if (player->GameOver) /* do not reanimate dead player */
5726 if (!player_relocated) /* no need to relocate the player */
5729 if (IS_PLAYER(jx, jy)) /* player already placed at new position */
5731 RemoveField(jx, jy); /* temporarily remove newly placed player */
5732 DrawLevelField(jx, jy);
5735 if (player->present)
5737 while (player->MovPos)
5739 ScrollPlayer(player, SCROLL_GO_ON);
5740 ScrollScreen(NULL, SCROLL_GO_ON);
5742 AdvanceFrameAndPlayerCounters(player->index_nr);
5747 Delay(wait_delay_value);
5750 DrawPlayer(player); /* needed here only to cleanup last field */
5751 DrawLevelField(player->jx, player->jy); /* remove player graphic */
5753 player->is_moving = FALSE;
5756 if (IS_CUSTOM_ELEMENT(old_element))
5757 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5759 player->index_bit, leave_side);
5761 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5763 player->index_bit, leave_side);
5765 Feld[jx][jy] = el_player;
5766 InitPlayerField(jx, jy, el_player, TRUE);
5768 if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5770 Feld[jx][jy] = element;
5771 InitField(jx, jy, FALSE);
5774 /* only visually relocate centered player */
5775 DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5776 FALSE, level.instant_relocation);
5778 TestIfPlayerTouchesBadThing(jx, jy);
5779 TestIfPlayerTouchesCustomElement(jx, jy);
5781 if (IS_CUSTOM_ELEMENT(element))
5782 CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5783 player->index_bit, enter_side);
5785 CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5786 player->index_bit, enter_side);
5789 if (player->is_switching)
5791 /* ensure that relocation while still switching an element does not cause
5792 a new element to be treated as also switched directly after relocation
5793 (this is important for teleporter switches that teleport the player to
5794 a place where another teleporter switch is in the same direction, which
5795 would then incorrectly be treated as immediately switched before the
5796 direction key that caused the switch was released) */
5798 player->switch_x += jx - old_jx;
5799 player->switch_y += jy - old_jy;
5804 void Explode(int ex, int ey, int phase, int mode)
5810 /* !!! eliminate this variable !!! */
5811 int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5813 if (game.explosions_delayed)
5815 ExplodeField[ex][ey] = mode;
5819 if (phase == EX_PHASE_START) /* initialize 'Store[][]' field */
5821 int center_element = Feld[ex][ey];
5822 int artwork_element, explosion_element; /* set these values later */
5825 /* --- This is only really needed (and now handled) in "Impact()". --- */
5826 /* do not explode moving elements that left the explode field in time */
5827 if (game.engine_version >= VERSION_IDENT(2,2,0,7) &&
5828 center_element == EL_EMPTY &&
5829 (mode == EX_TYPE_NORMAL || mode == EX_TYPE_CENTER))
5834 /* !!! at this place, the center element may be EL_BLOCKED !!! */
5835 if (mode == EX_TYPE_NORMAL ||
5836 mode == EX_TYPE_CENTER ||
5837 mode == EX_TYPE_CROSS)
5838 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5841 /* remove things displayed in background while burning dynamite */
5842 if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5845 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5847 /* put moving element to center field (and let it explode there) */
5848 center_element = MovingOrBlocked2Element(ex, ey);
5849 RemoveMovingField(ex, ey);
5850 Feld[ex][ey] = center_element;
5853 /* now "center_element" is finally determined -- set related values now */
5854 artwork_element = center_element; /* for custom player artwork */
5855 explosion_element = center_element; /* for custom player artwork */
5857 if (IS_PLAYER(ex, ey))
5859 int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5861 artwork_element = stored_player[player_nr].artwork_element;
5863 if (level.use_explosion_element[player_nr])
5865 explosion_element = level.explosion_element[player_nr];
5866 artwork_element = explosion_element;
5871 if (mode == EX_TYPE_NORMAL ||
5872 mode == EX_TYPE_CENTER ||
5873 mode == EX_TYPE_CROSS)
5874 PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5877 last_phase = element_info[explosion_element].explosion_delay + 1;
5879 for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5881 int xx = x - ex + 1;
5882 int yy = y - ey + 1;
5885 if (!IN_LEV_FIELD(x, y) ||
5886 (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5887 (mode == EX_TYPE_CROSS && (x != ex && y != ey)))
5890 element = Feld[x][y];
5892 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5894 element = MovingOrBlocked2Element(x, y);
5896 if (!IS_EXPLOSION_PROOF(element))
5897 RemoveMovingField(x, y);
5900 /* indestructible elements can only explode in center (but not flames) */
5901 if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5902 mode == EX_TYPE_BORDER)) ||
5903 element == EL_FLAMES)
5906 /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5907 behaviour, for example when touching a yamyam that explodes to rocks
5908 with active deadly shield, a rock is created under the player !!! */
5909 /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5911 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5912 (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5913 (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5915 if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5918 if (IS_ACTIVE_BOMB(element))
5920 /* re-activate things under the bomb like gate or penguin */
5921 Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5928 /* save walkable background elements while explosion on same tile */
5929 if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5930 (x != ex || y != ey || mode == EX_TYPE_BORDER))
5931 Back[x][y] = element;
5933 /* ignite explodable elements reached by other explosion */
5934 if (element == EL_EXPLOSION)
5935 element = Store2[x][y];
5937 if (AmoebaNr[x][y] &&
5938 (element == EL_AMOEBA_FULL ||
5939 element == EL_BD_AMOEBA ||
5940 element == EL_AMOEBA_GROWING))
5942 AmoebaCnt[AmoebaNr[x][y]]--;
5943 AmoebaCnt2[AmoebaNr[x][y]]--;
5948 if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5950 int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5952 Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5954 if (PLAYERINFO(ex, ey)->use_murphy)
5955 Store[x][y] = EL_EMPTY;
5958 /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5959 !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5960 else if (ELEM_IS_PLAYER(center_element))
5961 Store[x][y] = EL_EMPTY;
5962 else if (center_element == EL_YAMYAM)
5963 Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5964 else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5965 Store[x][y] = element_info[center_element].content.e[xx][yy];
5967 /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5968 (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5969 otherwise) -- FIX THIS !!! */
5970 else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5971 Store[x][y] = element_info[element].content.e[1][1];
5973 else if (!CAN_EXPLODE(element))
5974 Store[x][y] = element_info[element].content.e[1][1];
5977 Store[x][y] = EL_EMPTY;
5979 if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5980 center_element == EL_AMOEBA_TO_DIAMOND)
5981 Store2[x][y] = element;
5983 Feld[x][y] = EL_EXPLOSION;
5984 GfxElement[x][y] = artwork_element;
5986 ExplodePhase[x][y] = 1;
5987 ExplodeDelay[x][y] = last_phase;
5992 if (center_element == EL_YAMYAM)
5993 game.yamyam_content_nr =
5994 (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6006 GfxFrame[x][y] = 0; /* restart explosion animation */
6008 last_phase = ExplodeDelay[x][y];
6010 ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6014 /* activate this even in non-DEBUG version until cause for crash in
6015 getGraphicAnimationFrame() (see below) is found and eliminated */
6021 /* this can happen if the player leaves an explosion just in time */
6022 if (GfxElement[x][y] == EL_UNDEFINED)
6023 GfxElement[x][y] = EL_EMPTY;
6025 if (GfxElement[x][y] == EL_UNDEFINED)
6028 printf("Explode(): x = %d, y = %d: GfxElement == EL_UNDEFINED\n", x, y);
6029 printf("Explode(): This should never happen!\n");
6032 GfxElement[x][y] = EL_EMPTY;
6038 border_element = Store2[x][y];
6039 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6040 border_element = StorePlayer[x][y];
6042 if (phase == element_info[border_element].ignition_delay ||
6043 phase == last_phase)
6045 boolean border_explosion = FALSE;
6047 if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6048 !PLAYER_EXPLOSION_PROTECTED(x, y))
6050 KillPlayerUnlessExplosionProtected(x, y);
6051 border_explosion = TRUE;
6053 else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6055 Feld[x][y] = Store2[x][y];
6058 border_explosion = TRUE;
6060 else if (border_element == EL_AMOEBA_TO_DIAMOND)
6062 AmoebeUmwandeln(x, y);
6064 border_explosion = TRUE;
6067 /* if an element just explodes due to another explosion (chain-reaction),
6068 do not immediately end the new explosion when it was the last frame of
6069 the explosion (as it would be done in the following "if"-statement!) */
6070 if (border_explosion && phase == last_phase)
6074 if (phase == last_phase)
6078 element = Feld[x][y] = Store[x][y];
6079 Store[x][y] = Store2[x][y] = 0;
6080 GfxElement[x][y] = EL_UNDEFINED;
6082 /* player can escape from explosions and might therefore be still alive */
6083 if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6084 element <= EL_PLAYER_IS_EXPLODING_4)
6086 int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6087 int explosion_element = EL_PLAYER_1 + player_nr;
6088 int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6089 int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6091 if (level.use_explosion_element[player_nr])
6092 explosion_element = level.explosion_element[player_nr];
6094 Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6095 element_info[explosion_element].content.e[xx][yy]);
6098 /* restore probably existing indestructible background element */
6099 if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6100 element = Feld[x][y] = Back[x][y];
6103 MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6104 GfxDir[x][y] = MV_NONE;
6105 ChangeDelay[x][y] = 0;
6106 ChangePage[x][y] = -1;
6108 #if USE_NEW_CUSTOM_VALUE
6109 CustomValue[x][y] = 0;
6112 InitField_WithBug2(x, y, FALSE);
6114 TEST_DrawLevelField(x, y);
6116 TestIfElementTouchesCustomElement(x, y);
6118 if (GFX_CRUMBLED(element))
6119 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6121 if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6122 StorePlayer[x][y] = 0;
6124 if (ELEM_IS_PLAYER(element))
6125 RelocatePlayer(x, y, element);
6127 else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6129 int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6130 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
6133 TEST_DrawLevelFieldCrumbledSand(x, y);
6135 if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6137 DrawLevelElement(x, y, Back[x][y]);
6138 DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6140 else if (IS_WALKABLE_UNDER(Back[x][y]))
6142 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6143 DrawLevelElementThruMask(x, y, Back[x][y]);
6145 else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6146 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
6150 void DynaExplode(int ex, int ey)
6153 int dynabomb_element = Feld[ex][ey];
6154 int dynabomb_size = 1;
6155 boolean dynabomb_xl = FALSE;
6156 struct PlayerInfo *player;
6157 static int xy[4][2] =
6165 if (IS_ACTIVE_BOMB(dynabomb_element))
6167 player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6168 dynabomb_size = player->dynabomb_size;
6169 dynabomb_xl = player->dynabomb_xl;
6170 player->dynabombs_left++;
6173 Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6175 for (i = 0; i < NUM_DIRECTIONS; i++)
6177 for (j = 1; j <= dynabomb_size; j++)
6179 int x = ex + j * xy[i][0];
6180 int y = ey + j * xy[i][1];
6183 if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
6186 element = Feld[x][y];
6188 /* do not restart explosions of fields with active bombs */
6189 if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6192 Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6194 if (element != EL_EMPTY && element != EL_EXPLOSION &&
6195 !IS_DIGGABLE(element) && !dynabomb_xl)
6201 void Bang(int x, int y)
6203 int element = MovingOrBlocked2Element(x, y);
6204 int explosion_type = EX_TYPE_NORMAL;
6206 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6208 struct PlayerInfo *player = PLAYERINFO(x, y);
6210 #if USE_FIX_CE_ACTION_WITH_PLAYER
6211 element = Feld[x][y] = player->initial_element;
6213 element = Feld[x][y] = (player->use_murphy ? EL_SP_MURPHY :
6214 player->element_nr);
6217 if (level.use_explosion_element[player->index_nr])
6219 int explosion_element = level.explosion_element[player->index_nr];
6221 if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6222 explosion_type = EX_TYPE_CROSS;
6223 else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6224 explosion_type = EX_TYPE_CENTER;
6232 case EL_BD_BUTTERFLY:
6235 case EL_DARK_YAMYAM:
6239 RaiseScoreElement(element);
6242 case EL_DYNABOMB_PLAYER_1_ACTIVE:
6243 case EL_DYNABOMB_PLAYER_2_ACTIVE:
6244 case EL_DYNABOMB_PLAYER_3_ACTIVE:
6245 case EL_DYNABOMB_PLAYER_4_ACTIVE:
6246 case EL_DYNABOMB_INCREASE_NUMBER:
6247 case EL_DYNABOMB_INCREASE_SIZE:
6248 case EL_DYNABOMB_INCREASE_POWER:
6249 explosion_type = EX_TYPE_DYNA;
6252 case EL_DC_LANDMINE:
6254 case EL_EM_EXIT_OPEN:
6255 case EL_EM_STEEL_EXIT_OPEN:
6257 explosion_type = EX_TYPE_CENTER;
6262 case EL_LAMP_ACTIVE:
6263 case EL_AMOEBA_TO_DIAMOND:
6264 if (!IS_PLAYER(x, y)) /* penguin and player may be at same field */
6265 explosion_type = EX_TYPE_CENTER;
6269 if (element_info[element].explosion_type == EXPLODES_CROSS)
6270 explosion_type = EX_TYPE_CROSS;
6271 else if (element_info[element].explosion_type == EXPLODES_1X1)
6272 explosion_type = EX_TYPE_CENTER;
6276 if (explosion_type == EX_TYPE_DYNA)
6279 Explode(x, y, EX_PHASE_START, explosion_type);
6281 CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6284 void SplashAcid(int x, int y)
6286 if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6287 (!IN_LEV_FIELD(x - 1, y - 2) ||
6288 !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6289 Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6291 if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6292 (!IN_LEV_FIELD(x + 1, y - 2) ||
6293 !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6294 Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6296 PlayLevelSound(x, y, SND_ACID_SPLASHING);
6299 static void InitBeltMovement()
6301 static int belt_base_element[4] =
6303 EL_CONVEYOR_BELT_1_LEFT,
6304 EL_CONVEYOR_BELT_2_LEFT,
6305 EL_CONVEYOR_BELT_3_LEFT,
6306 EL_CONVEYOR_BELT_4_LEFT
6308 static int belt_base_active_element[4] =
6310 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6311 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6312 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6313 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6318 /* set frame order for belt animation graphic according to belt direction */
6319 for (i = 0; i < NUM_BELTS; i++)
6323 for (j = 0; j < NUM_BELT_PARTS; j++)
6325 int element = belt_base_active_element[belt_nr] + j;
6326 int graphic_1 = el2img(element);
6327 int graphic_2 = el2panelimg(element);
6329 if (game.belt_dir[i] == MV_LEFT)
6331 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6332 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6336 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6337 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6342 SCAN_PLAYFIELD(x, y)
6344 int element = Feld[x][y];
6346 for (i = 0; i < NUM_BELTS; i++)
6348 if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6350 int e_belt_nr = getBeltNrFromBeltElement(element);
6353 if (e_belt_nr == belt_nr)
6355 int belt_part = Feld[x][y] - belt_base_element[belt_nr];
6357 Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
6364 static void ToggleBeltSwitch(int x, int y)
6366 static int belt_base_element[4] =
6368 EL_CONVEYOR_BELT_1_LEFT,
6369 EL_CONVEYOR_BELT_2_LEFT,
6370 EL_CONVEYOR_BELT_3_LEFT,
6371 EL_CONVEYOR_BELT_4_LEFT
6373 static int belt_base_active_element[4] =
6375 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6376 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6377 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6378 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6380 static int belt_base_switch_element[4] =
6382 EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6383 EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6384 EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6385 EL_CONVEYOR_BELT_4_SWITCH_LEFT
6387 static int belt_move_dir[4] =
6395 int element = Feld[x][y];
6396 int belt_nr = getBeltNrFromBeltSwitchElement(element);
6397 int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6398 int belt_dir = belt_move_dir[belt_dir_nr];
6401 if (!IS_BELT_SWITCH(element))
6404 game.belt_dir_nr[belt_nr] = belt_dir_nr;
6405 game.belt_dir[belt_nr] = belt_dir;
6407 if (belt_dir_nr == 3)
6410 /* set frame order for belt animation graphic according to belt direction */
6411 for (i = 0; i < NUM_BELT_PARTS; i++)
6413 int element = belt_base_active_element[belt_nr] + i;
6414 int graphic_1 = el2img(element);
6415 int graphic_2 = el2panelimg(element);
6417 if (belt_dir == MV_LEFT)
6419 graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6420 graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6424 graphic_info[graphic_1].anim_mode |= ANIM_REVERSE;
6425 graphic_info[graphic_2].anim_mode |= ANIM_REVERSE;
6429 SCAN_PLAYFIELD(xx, yy)
6431 int element = Feld[xx][yy];
6433 if (IS_BELT_SWITCH(element))
6435 int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6437 if (e_belt_nr == belt_nr)
6439 Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6440 TEST_DrawLevelField(xx, yy);
6443 else if (IS_BELT(element) && belt_dir != MV_NONE)
6445 int e_belt_nr = getBeltNrFromBeltElement(element);
6447 if (e_belt_nr == belt_nr)
6449 int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
6451 Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6452 TEST_DrawLevelField(xx, yy);
6455 else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6457 int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6459 if (e_belt_nr == belt_nr)
6461 int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
6463 Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
6464 TEST_DrawLevelField(xx, yy);
6470 static void ToggleSwitchgateSwitch(int x, int y)
6474 game.switchgate_pos = !game.switchgate_pos;
6476 SCAN_PLAYFIELD(xx, yy)
6478 int element = Feld[xx][yy];
6480 #if !USE_BOTH_SWITCHGATE_SWITCHES
6481 if (element == EL_SWITCHGATE_SWITCH_UP ||
6482 element == EL_SWITCHGATE_SWITCH_DOWN)
6484 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6485 TEST_DrawLevelField(xx, yy);
6487 else if (element == EL_DC_SWITCHGATE_SWITCH_UP ||
6488 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6490 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
6491 TEST_DrawLevelField(xx, yy);
6494 if (element == EL_SWITCHGATE_SWITCH_UP)
6496 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6497 TEST_DrawLevelField(xx, yy);
6499 else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6501 Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6502 TEST_DrawLevelField(xx, yy);
6504 else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6506 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6507 TEST_DrawLevelField(xx, yy);
6509 else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6511 Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6512 TEST_DrawLevelField(xx, yy);
6515 else if (element == EL_SWITCHGATE_OPEN ||
6516 element == EL_SWITCHGATE_OPENING)
6518 Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
6520 PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6522 else if (element == EL_SWITCHGATE_CLOSED ||
6523 element == EL_SWITCHGATE_CLOSING)
6525 Feld[xx][yy] = EL_SWITCHGATE_OPENING;
6527 PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6532 static int getInvisibleActiveFromInvisibleElement(int element)
6534 return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6535 element == EL_INVISIBLE_WALL ? EL_INVISIBLE_WALL_ACTIVE :
6536 element == EL_INVISIBLE_SAND ? EL_INVISIBLE_SAND_ACTIVE :
6540 static int getInvisibleFromInvisibleActiveElement(int element)
6542 return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6543 element == EL_INVISIBLE_WALL_ACTIVE ? EL_INVISIBLE_WALL :
6544 element == EL_INVISIBLE_SAND_ACTIVE ? EL_INVISIBLE_SAND :
6548 static void RedrawAllLightSwitchesAndInvisibleElements()
6552 SCAN_PLAYFIELD(x, y)
6554 int element = Feld[x][y];
6556 if (element == EL_LIGHT_SWITCH &&
6557 game.light_time_left > 0)
6559 Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6560 TEST_DrawLevelField(x, y);
6562 else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6563 game.light_time_left == 0)
6565 Feld[x][y] = EL_LIGHT_SWITCH;
6566 TEST_DrawLevelField(x, y);
6568 else if (element == EL_EMC_DRIPPER &&
6569 game.light_time_left > 0)
6571 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6572 TEST_DrawLevelField(x, y);
6574 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6575 game.light_time_left == 0)
6577 Feld[x][y] = EL_EMC_DRIPPER;
6578 TEST_DrawLevelField(x, y);
6580 else if (element == EL_INVISIBLE_STEELWALL ||
6581 element == EL_INVISIBLE_WALL ||
6582 element == EL_INVISIBLE_SAND)
6584 if (game.light_time_left > 0)
6585 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6587 TEST_DrawLevelField(x, y);
6589 /* uncrumble neighbour fields, if needed */
6590 if (element == EL_INVISIBLE_SAND)
6591 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6593 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6594 element == EL_INVISIBLE_WALL_ACTIVE ||
6595 element == EL_INVISIBLE_SAND_ACTIVE)
6597 if (game.light_time_left == 0)
6598 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6600 TEST_DrawLevelField(x, y);
6602 /* re-crumble neighbour fields, if needed */
6603 if (element == EL_INVISIBLE_SAND)
6604 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6609 static void RedrawAllInvisibleElementsForLenses()
6613 SCAN_PLAYFIELD(x, y)
6615 int element = Feld[x][y];
6617 if (element == EL_EMC_DRIPPER &&
6618 game.lenses_time_left > 0)
6620 Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
6621 TEST_DrawLevelField(x, y);
6623 else if (element == EL_EMC_DRIPPER_ACTIVE &&
6624 game.lenses_time_left == 0)
6626 Feld[x][y] = EL_EMC_DRIPPER;
6627 TEST_DrawLevelField(x, y);
6629 else if (element == EL_INVISIBLE_STEELWALL ||
6630 element == EL_INVISIBLE_WALL ||
6631 element == EL_INVISIBLE_SAND)
6633 if (game.lenses_time_left > 0)
6634 Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
6636 TEST_DrawLevelField(x, y);
6638 /* uncrumble neighbour fields, if needed */
6639 if (element == EL_INVISIBLE_SAND)
6640 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6642 else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6643 element == EL_INVISIBLE_WALL_ACTIVE ||
6644 element == EL_INVISIBLE_SAND_ACTIVE)
6646 if (game.lenses_time_left == 0)
6647 Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
6649 TEST_DrawLevelField(x, y);
6651 /* re-crumble neighbour fields, if needed */
6652 if (element == EL_INVISIBLE_SAND)
6653 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
6658 static void RedrawAllInvisibleElementsForMagnifier()
6662 SCAN_PLAYFIELD(x, y)
6664 int element = Feld[x][y];
6666 if (element == EL_EMC_FAKE_GRASS &&
6667 game.magnify_time_left > 0)
6669 Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6670 TEST_DrawLevelField(x, y);
6672 else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6673 game.magnify_time_left == 0)
6675 Feld[x][y] = EL_EMC_FAKE_GRASS;
6676 TEST_DrawLevelField(x, y);
6678 else if (IS_GATE_GRAY(element) &&
6679 game.magnify_time_left > 0)
6681 Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
6682 element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6683 IS_EM_GATE_GRAY(element) ?
6684 element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6685 IS_EMC_GATE_GRAY(element) ?
6686 element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6687 IS_DC_GATE_GRAY(element) ?
6688 EL_DC_GATE_WHITE_GRAY_ACTIVE :
6690 TEST_DrawLevelField(x, y);
6692 else if (IS_GATE_GRAY_ACTIVE(element) &&
6693 game.magnify_time_left == 0)
6695 Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6696 element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6697 IS_EM_GATE_GRAY_ACTIVE(element) ?
6698 element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6699 IS_EMC_GATE_GRAY_ACTIVE(element) ?
6700 element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6701 IS_DC_GATE_GRAY_ACTIVE(element) ?
6702 EL_DC_GATE_WHITE_GRAY :
6704 TEST_DrawLevelField(x, y);
6709 static void ToggleLightSwitch(int x, int y)
6711 int element = Feld[x][y];
6713 game.light_time_left =
6714 (element == EL_LIGHT_SWITCH ?
6715 level.time_light * FRAMES_PER_SECOND : 0);
6717 RedrawAllLightSwitchesAndInvisibleElements();
6720 static void ActivateTimegateSwitch(int x, int y)
6724 game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6726 SCAN_PLAYFIELD(xx, yy)
6728 int element = Feld[xx][yy];
6730 if (element == EL_TIMEGATE_CLOSED ||
6731 element == EL_TIMEGATE_CLOSING)
6733 Feld[xx][yy] = EL_TIMEGATE_OPENING;
6734 PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6738 else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6740 Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6741 TEST_DrawLevelField(xx, yy);
6748 Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6749 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6751 Feld[x][y] = EL_TIMEGATE_SWITCH_ACTIVE;
6755 void Impact(int x, int y)
6757 boolean last_line = (y == lev_fieldy - 1);
6758 boolean object_hit = FALSE;
6759 boolean impact = (last_line || object_hit);
6760 int element = Feld[x][y];
6761 int smashed = EL_STEELWALL;
6763 if (!last_line) /* check if element below was hit */
6765 if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6768 object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6769 MovDir[x][y + 1] != MV_DOWN ||
6770 MovPos[x][y + 1] <= TILEY / 2));
6772 /* do not smash moving elements that left the smashed field in time */
6773 if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6774 ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6777 #if USE_QUICKSAND_IMPACT_BUGFIX
6778 if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6780 RemoveMovingField(x, y + 1);
6781 Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6782 Feld[x][y + 2] = EL_ROCK;
6783 TEST_DrawLevelField(x, y + 2);
6788 if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6790 RemoveMovingField(x, y + 1);
6791 Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6792 Feld[x][y + 2] = EL_ROCK;
6793 TEST_DrawLevelField(x, y + 2);
6800 smashed = MovingOrBlocked2Element(x, y + 1);
6802 impact = (last_line || object_hit);
6805 if (!last_line && smashed == EL_ACID) /* element falls into acid */
6807 SplashAcid(x, y + 1);
6811 /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6812 /* only reset graphic animation if graphic really changes after impact */
6814 el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6816 ResetGfxAnimation(x, y);
6817 TEST_DrawLevelField(x, y);
6820 if (impact && CAN_EXPLODE_IMPACT(element))
6825 else if (impact && element == EL_PEARL &&
6826 smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6828 ResetGfxAnimation(x, y);
6830 Feld[x][y] = EL_PEARL_BREAKING;
6831 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6834 else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6836 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6841 if (impact && element == EL_AMOEBA_DROP)
6843 if (object_hit && IS_PLAYER(x, y + 1))
6844 KillPlayerUnlessEnemyProtected(x, y + 1);
6845 else if (object_hit && smashed == EL_PENGUIN)
6849 Feld[x][y] = EL_AMOEBA_GROWING;
6850 Store[x][y] = EL_AMOEBA_WET;
6852 ResetRandomAnimationValue(x, y);
6857 if (object_hit) /* check which object was hit */
6859 if ((CAN_PASS_MAGIC_WALL(element) &&
6860 (smashed == EL_MAGIC_WALL ||
6861 smashed == EL_BD_MAGIC_WALL)) ||
6862 (CAN_PASS_DC_MAGIC_WALL(element) &&
6863 smashed == EL_DC_MAGIC_WALL))
6866 int activated_magic_wall =
6867 (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6868 smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6869 EL_DC_MAGIC_WALL_ACTIVE);
6871 /* activate magic wall / mill */
6872 SCAN_PLAYFIELD(xx, yy)
6874 if (Feld[xx][yy] == smashed)
6875 Feld[xx][yy] = activated_magic_wall;
6878 game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6879 game.magic_wall_active = TRUE;
6881 PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6882 SND_MAGIC_WALL_ACTIVATING :
6883 smashed == EL_BD_MAGIC_WALL ?
6884 SND_BD_MAGIC_WALL_ACTIVATING :
6885 SND_DC_MAGIC_WALL_ACTIVATING));
6888 if (IS_PLAYER(x, y + 1))
6890 if (CAN_SMASH_PLAYER(element))
6892 KillPlayerUnlessEnemyProtected(x, y + 1);
6896 else if (smashed == EL_PENGUIN)
6898 if (CAN_SMASH_PLAYER(element))
6904 else if (element == EL_BD_DIAMOND)
6906 if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6912 else if (((element == EL_SP_INFOTRON ||
6913 element == EL_SP_ZONK) &&
6914 (smashed == EL_SP_SNIKSNAK ||
6915 smashed == EL_SP_ELECTRON ||
6916 smashed == EL_SP_DISK_ORANGE)) ||
6917 (element == EL_SP_INFOTRON &&
6918 smashed == EL_SP_DISK_YELLOW))
6923 else if (CAN_SMASH_EVERYTHING(element))
6925 if (IS_CLASSIC_ENEMY(smashed) ||
6926 CAN_EXPLODE_SMASHED(smashed))
6931 else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6933 if (smashed == EL_LAMP ||
6934 smashed == EL_LAMP_ACTIVE)
6939 else if (smashed == EL_NUT)
6941 Feld[x][y + 1] = EL_NUT_BREAKING;
6942 PlayLevelSound(x, y, SND_NUT_BREAKING);
6943 RaiseScoreElement(EL_NUT);
6946 else if (smashed == EL_PEARL)
6948 ResetGfxAnimation(x, y);
6950 Feld[x][y + 1] = EL_PEARL_BREAKING;
6951 PlayLevelSound(x, y, SND_PEARL_BREAKING);
6954 else if (smashed == EL_DIAMOND)
6956 Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6957 PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6960 else if (IS_BELT_SWITCH(smashed))
6962 ToggleBeltSwitch(x, y + 1);
6964 else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6965 smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6966 smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6967 smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6969 ToggleSwitchgateSwitch(x, y + 1);
6971 else if (smashed == EL_LIGHT_SWITCH ||
6972 smashed == EL_LIGHT_SWITCH_ACTIVE)
6974 ToggleLightSwitch(x, y + 1);
6979 TestIfElementSmashesCustomElement(x, y, MV_DOWN);
6982 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6984 CheckElementChangeBySide(x, y + 1, smashed, element,
6985 CE_SWITCHED, CH_SIDE_TOP);
6986 CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6992 CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6997 /* play sound of magic wall / mill */
6999 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7000 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7001 Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7003 if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7004 PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7005 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7006 PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7007 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7008 PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7013 /* play sound of object that hits the ground */
7014 if (last_line || object_hit)
7015 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7018 inline static void TurnRoundExt(int x, int y)
7030 { 0, 0 }, { 0, 0 }, { 0, 0 },
7035 int left, right, back;
7039 { MV_DOWN, MV_UP, MV_RIGHT },
7040 { MV_UP, MV_DOWN, MV_LEFT },
7042 { MV_LEFT, MV_RIGHT, MV_DOWN },
7046 { MV_RIGHT, MV_LEFT, MV_UP }
7049 int element = Feld[x][y];
7050 int move_pattern = element_info[element].move_pattern;
7052 int old_move_dir = MovDir[x][y];
7053 int left_dir = turn[old_move_dir].left;
7054 int right_dir = turn[old_move_dir].right;
7055 int back_dir = turn[old_move_dir].back;
7057 int left_dx = move_xy[left_dir].dx, left_dy = move_xy[left_dir].dy;
7058 int right_dx = move_xy[right_dir].dx, right_dy = move_xy[right_dir].dy;
7059 int move_dx = move_xy[old_move_dir].dx, move_dy = move_xy[old_move_dir].dy;
7060 int back_dx = move_xy[back_dir].dx, back_dy = move_xy[back_dir].dy;
7062 int left_x = x + left_dx, left_y = y + left_dy;
7063 int right_x = x + right_dx, right_y = y + right_dy;
7064 int move_x = x + move_dx, move_y = y + move_dy;
7068 if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7070 TestIfBadThingTouchesOtherBadThing(x, y);
7072 if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7073 MovDir[x][y] = right_dir;
7074 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7075 MovDir[x][y] = left_dir;
7077 if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7079 else if (element == EL_BD_BUTTERFLY) /* && MovDir[x][y] == left_dir) */
7082 else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7084 TestIfBadThingTouchesOtherBadThing(x, y);
7086 if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7087 MovDir[x][y] = left_dir;
7088 else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7089 MovDir[x][y] = right_dir;
7091 if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7093 else if (element == EL_BD_FIREFLY) /* && MovDir[x][y] == right_dir) */
7096 else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7098 TestIfBadThingTouchesOtherBadThing(x, y);
7100 if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7101 MovDir[x][y] = left_dir;
7102 else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7103 MovDir[x][y] = right_dir;
7105 if (MovDir[x][y] != old_move_dir)
7108 else if (element == EL_YAMYAM)
7110 boolean can_turn_left = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7111 boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7113 if (can_turn_left && can_turn_right)
7114 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7115 else if (can_turn_left)
7116 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7117 else if (can_turn_right)
7118 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7120 MovDir[x][y] = back_dir;
7122 MovDelay[x][y] = 16 + 16 * RND(3);
7124 else if (element == EL_DARK_YAMYAM)
7126 boolean can_turn_left = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7128 boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7131 if (can_turn_left && can_turn_right)
7132 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7133 else if (can_turn_left)
7134 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7135 else if (can_turn_right)
7136 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7138 MovDir[x][y] = back_dir;
7140 MovDelay[x][y] = 16 + 16 * RND(3);
7142 else if (element == EL_PACMAN)
7144 boolean can_turn_left = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7145 boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7147 if (can_turn_left && can_turn_right)
7148 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7149 else if (can_turn_left)
7150 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7151 else if (can_turn_right)
7152 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7154 MovDir[x][y] = back_dir;
7156 MovDelay[x][y] = 6 + RND(40);
7158 else if (element == EL_PIG)
7160 boolean can_turn_left = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7161 boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7162 boolean can_move_on = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7163 boolean should_turn_left, should_turn_right, should_move_on;
7165 int rnd = RND(rnd_value);
7167 should_turn_left = (can_turn_left &&
7169 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7170 y + back_dy + left_dy)));
7171 should_turn_right = (can_turn_right &&
7173 IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7174 y + back_dy + right_dy)));
7175 should_move_on = (can_move_on &&
7178 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7179 y + move_dy + left_dy) ||
7180 IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7181 y + move_dy + right_dy)));
7183 if (should_turn_left || should_turn_right || should_move_on)
7185 if (should_turn_left && should_turn_right && should_move_on)
7186 MovDir[x][y] = (rnd < rnd_value / 3 ? left_dir :
7187 rnd < 2 * rnd_value / 3 ? right_dir :
7189 else if (should_turn_left && should_turn_right)
7190 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7191 else if (should_turn_left && should_move_on)
7192 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7193 else if (should_turn_right && should_move_on)
7194 MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7195 else if (should_turn_left)
7196 MovDir[x][y] = left_dir;
7197 else if (should_turn_right)
7198 MovDir[x][y] = right_dir;
7199 else if (should_move_on)
7200 MovDir[x][y] = old_move_dir;
7202 else if (can_move_on && rnd > rnd_value / 8)
7203 MovDir[x][y] = old_move_dir;
7204 else if (can_turn_left && can_turn_right)
7205 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7206 else if (can_turn_left && rnd > rnd_value / 8)
7207 MovDir[x][y] = left_dir;
7208 else if (can_turn_right && rnd > rnd_value/8)
7209 MovDir[x][y] = right_dir;
7211 MovDir[x][y] = back_dir;
7213 xx = x + move_xy[MovDir[x][y]].dx;
7214 yy = y + move_xy[MovDir[x][y]].dy;
7216 if (!IN_LEV_FIELD(xx, yy) ||
7217 (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
7218 MovDir[x][y] = old_move_dir;
7222 else if (element == EL_DRAGON)
7224 boolean can_turn_left = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7225 boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7226 boolean can_move_on = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7228 int rnd = RND(rnd_value);
7230 if (can_move_on && rnd > rnd_value / 8)
7231 MovDir[x][y] = old_move_dir;
7232 else if (can_turn_left && can_turn_right)
7233 MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7234 else if (can_turn_left && rnd > rnd_value / 8)
7235 MovDir[x][y] = left_dir;
7236 else if (can_turn_right && rnd > rnd_value / 8)
7237 MovDir[x][y] = right_dir;
7239 MovDir[x][y] = back_dir;
7241 xx = x + move_xy[MovDir[x][y]].dx;
7242 yy = y + move_xy[MovDir[x][y]].dy;
7244 if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7245 MovDir[x][y] = old_move_dir;
7249 else if (element == EL_MOLE)
7251 boolean can_move_on =
7252 (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7253 IS_AMOEBOID(Feld[move_x][move_y]) ||
7254 Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
7257 boolean can_turn_left =
7258 (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7259 IS_AMOEBOID(Feld[left_x][left_y])));
7261 boolean can_turn_right =
7262 (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7263 IS_AMOEBOID(Feld[right_x][right_y])));
7265 if (can_turn_left && can_turn_right)
7266 MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7267 else if (can_turn_left)
7268 MovDir[x][y] = left_dir;
7270 MovDir[x][y] = right_dir;
7273 if (MovDir[x][y] != old_move_dir)
7276 else if (element == EL_BALLOON)
7278 MovDir[x][y] = game.wind_direction;
7281 else if (element == EL_SPRING)
7283 #if USE_NEW_SPRING_BUMPER
7284 if (MovDir[x][y] & MV_HORIZONTAL)
7286 if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7287 !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7289 Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7290 ResetGfxAnimation(move_x, move_y);
7291 TEST_DrawLevelField(move_x, move_y);
7293 MovDir[x][y] = back_dir;
7295 else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7296 SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7297 MovDir[x][y] = MV_NONE;
7300 if (MovDir[x][y] & MV_HORIZONTAL &&
7301 (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7302 SPRING_CAN_ENTER_FIELD(element, x, y + 1)))
7303 MovDir[x][y] = MV_NONE;
7308 else if (element == EL_ROBOT ||
7309 element == EL_SATELLITE ||
7310 element == EL_PENGUIN ||
7311 element == EL_EMC_ANDROID)
7313 int attr_x = -1, attr_y = -1;
7324 for (i = 0; i < MAX_PLAYERS; i++)
7326 struct PlayerInfo *player = &stored_player[i];
7327 int jx = player->jx, jy = player->jy;
7329 if (!player->active)
7333 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7341 if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
7342 (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
7343 game.engine_version < VERSION_IDENT(3,1,0,0)))
7349 if (element == EL_PENGUIN)
7352 static int xy[4][2] =
7360 for (i = 0; i < NUM_DIRECTIONS; i++)
7362 int ex = x + xy[i][0];
7363 int ey = y + xy[i][1];
7365 if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
7366 Feld[ex][ey] == EL_EM_EXIT_OPEN ||
7367 Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
7368 Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7377 MovDir[x][y] = MV_NONE;
7379 MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
7380 else if (attr_x > x)
7381 MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
7383 MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
7384 else if (attr_y > y)
7385 MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
7387 if (element == EL_ROBOT)
7391 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7392 MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7393 Moving2Blocked(x, y, &newx, &newy);
7395 if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7396 MovDelay[x][y] = 8 + 8 * !RND(3);
7398 MovDelay[x][y] = 16;
7400 else if (element == EL_PENGUIN)
7406 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7408 boolean first_horiz = RND(2);
7409 int new_move_dir = MovDir[x][y];
7412 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7413 Moving2Blocked(x, y, &newx, &newy);
7415 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7419 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7420 Moving2Blocked(x, y, &newx, &newy);
7422 if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7425 MovDir[x][y] = old_move_dir;
7429 else if (element == EL_SATELLITE)
7435 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7437 boolean first_horiz = RND(2);
7438 int new_move_dir = MovDir[x][y];
7441 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7442 Moving2Blocked(x, y, &newx, &newy);
7444 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7448 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7449 Moving2Blocked(x, y, &newx, &newy);
7451 if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7454 MovDir[x][y] = old_move_dir;
7458 else if (element == EL_EMC_ANDROID)
7460 static int check_pos[16] =
7462 -1, /* 0 => (invalid) */
7463 7, /* 1 => MV_LEFT */
7464 3, /* 2 => MV_RIGHT */
7465 -1, /* 3 => (invalid) */
7467 0, /* 5 => MV_LEFT | MV_UP */
7468 2, /* 6 => MV_RIGHT | MV_UP */
7469 -1, /* 7 => (invalid) */
7470 5, /* 8 => MV_DOWN */
7471 6, /* 9 => MV_LEFT | MV_DOWN */
7472 4, /* 10 => MV_RIGHT | MV_DOWN */
7473 -1, /* 11 => (invalid) */
7474 -1, /* 12 => (invalid) */
7475 -1, /* 13 => (invalid) */
7476 -1, /* 14 => (invalid) */
7477 -1, /* 15 => (invalid) */
7485 { -1, -1, MV_LEFT | MV_UP },
7487 { +1, -1, MV_RIGHT | MV_UP },
7488 { +1, 0, MV_RIGHT },
7489 { +1, +1, MV_RIGHT | MV_DOWN },
7491 { -1, +1, MV_LEFT | MV_DOWN },
7494 int start_pos, check_order;
7495 boolean can_clone = FALSE;
7498 /* check if there is any free field around current position */
7499 for (i = 0; i < 8; i++)
7501 int newx = x + check_xy[i].dx;
7502 int newy = y + check_xy[i].dy;
7504 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7512 if (can_clone) /* randomly find an element to clone */
7516 start_pos = check_pos[RND(8)];
7517 check_order = (RND(2) ? -1 : +1);
7519 for (i = 0; i < 8; i++)
7521 int pos_raw = start_pos + i * check_order;
7522 int pos = (pos_raw + 8) % 8;
7523 int newx = x + check_xy[pos].dx;
7524 int newy = y + check_xy[pos].dy;
7526 if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7528 element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7529 element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7531 Store[x][y] = Feld[newx][newy];
7540 if (can_clone) /* randomly find a direction to move */
7544 start_pos = check_pos[RND(8)];
7545 check_order = (RND(2) ? -1 : +1);
7547 for (i = 0; i < 8; i++)
7549 int pos_raw = start_pos + i * check_order;
7550 int pos = (pos_raw + 8) % 8;
7551 int newx = x + check_xy[pos].dx;
7552 int newy = y + check_xy[pos].dy;
7553 int new_move_dir = check_xy[pos].dir;
7555 if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7557 MovDir[x][y] = new_move_dir;
7558 MovDelay[x][y] = level.android_clone_time * 8 + 1;
7567 if (can_clone) /* cloning and moving successful */
7570 /* cannot clone -- try to move towards player */
7572 start_pos = check_pos[MovDir[x][y] & 0x0f];
7573 check_order = (RND(2) ? -1 : +1);
7575 for (i = 0; i < 3; i++)
7577 /* first check start_pos, then previous/next or (next/previous) pos */
7578 int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7579 int pos = (pos_raw + 8) % 8;
7580 int newx = x + check_xy[pos].dx;
7581 int newy = y + check_xy[pos].dy;
7582 int new_move_dir = check_xy[pos].dir;
7584 if (IS_PLAYER(newx, newy))
7587 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7589 MovDir[x][y] = new_move_dir;
7590 MovDelay[x][y] = level.android_move_time * 8 + 1;
7597 else if (move_pattern == MV_TURNING_LEFT ||
7598 move_pattern == MV_TURNING_RIGHT ||
7599 move_pattern == MV_TURNING_LEFT_RIGHT ||
7600 move_pattern == MV_TURNING_RIGHT_LEFT ||
7601 move_pattern == MV_TURNING_RANDOM ||
7602 move_pattern == MV_ALL_DIRECTIONS)
7604 boolean can_turn_left =
7605 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7606 boolean can_turn_right =
7607 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7609 if (element_info[element].move_stepsize == 0) /* "not moving" */
7612 if (move_pattern == MV_TURNING_LEFT)
7613 MovDir[x][y] = left_dir;
7614 else if (move_pattern == MV_TURNING_RIGHT)
7615 MovDir[x][y] = right_dir;
7616 else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7617 MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7618 else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7619 MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7620 else if (move_pattern == MV_TURNING_RANDOM)
7621 MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7622 can_turn_right && !can_turn_left ? right_dir :
7623 RND(2) ? left_dir : right_dir);
7624 else if (can_turn_left && can_turn_right)
7625 MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7626 else if (can_turn_left)
7627 MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7628 else if (can_turn_right)
7629 MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7631 MovDir[x][y] = back_dir;
7633 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7635 else if (move_pattern == MV_HORIZONTAL ||
7636 move_pattern == MV_VERTICAL)
7638 if (move_pattern & old_move_dir)
7639 MovDir[x][y] = back_dir;
7640 else if (move_pattern == MV_HORIZONTAL)
7641 MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7642 else if (move_pattern == MV_VERTICAL)
7643 MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7645 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7647 else if (move_pattern & MV_ANY_DIRECTION)
7649 MovDir[x][y] = move_pattern;
7650 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7652 else if (move_pattern & MV_WIND_DIRECTION)
7654 MovDir[x][y] = game.wind_direction;
7655 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7657 else if (move_pattern == MV_ALONG_LEFT_SIDE)
7659 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7660 MovDir[x][y] = left_dir;
7661 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7662 MovDir[x][y] = right_dir;
7664 if (MovDir[x][y] != old_move_dir)
7665 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7667 else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7669 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7670 MovDir[x][y] = right_dir;
7671 else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7672 MovDir[x][y] = left_dir;
7674 if (MovDir[x][y] != old_move_dir)
7675 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677 else if (move_pattern == MV_TOWARDS_PLAYER ||
7678 move_pattern == MV_AWAY_FROM_PLAYER)
7680 int attr_x = -1, attr_y = -1;
7682 boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7693 for (i = 0; i < MAX_PLAYERS; i++)
7695 struct PlayerInfo *player = &stored_player[i];
7696 int jx = player->jx, jy = player->jy;
7698 if (!player->active)
7702 ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7710 MovDir[x][y] = MV_NONE;
7712 MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7713 else if (attr_x > x)
7714 MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7716 MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7717 else if (attr_y > y)
7718 MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7720 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7722 if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7724 boolean first_horiz = RND(2);
7725 int new_move_dir = MovDir[x][y];
7727 if (element_info[element].move_stepsize == 0) /* "not moving" */
7729 first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7730 MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7736 new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7737 Moving2Blocked(x, y, &newx, &newy);
7739 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7743 new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7744 Moving2Blocked(x, y, &newx, &newy);
7746 if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7749 MovDir[x][y] = old_move_dir;
7752 else if (move_pattern == MV_WHEN_PUSHED ||
7753 move_pattern == MV_WHEN_DROPPED)
7755 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7756 MovDir[x][y] = MV_NONE;
7760 else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7762 static int test_xy[7][2] =
7772 static int test_dir[7] =
7782 boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7783 int move_preference = -1000000; /* start with very low preference */
7784 int new_move_dir = MV_NONE;
7785 int start_test = RND(4);
7788 for (i = 0; i < NUM_DIRECTIONS; i++)
7790 int move_dir = test_dir[start_test + i];
7791 int move_dir_preference;
7793 xx = x + test_xy[start_test + i][0];
7794 yy = y + test_xy[start_test + i][1];
7796 if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7797 (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7799 new_move_dir = move_dir;
7804 if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7807 move_dir_preference = -1 * RunnerVisit[xx][yy];
7808 if (hunter_mode && PlayerVisit[xx][yy] > 0)
7809 move_dir_preference = PlayerVisit[xx][yy];
7811 if (move_dir_preference > move_preference)
7813 /* prefer field that has not been visited for the longest time */
7814 move_preference = move_dir_preference;
7815 new_move_dir = move_dir;
7817 else if (move_dir_preference == move_preference &&
7818 move_dir == old_move_dir)
7820 /* prefer last direction when all directions are preferred equally */
7821 move_preference = move_dir_preference;
7822 new_move_dir = move_dir;
7826 MovDir[x][y] = new_move_dir;
7827 if (old_move_dir != new_move_dir)
7828 MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7832 static void TurnRound(int x, int y)
7834 int direction = MovDir[x][y];
7838 GfxDir[x][y] = MovDir[x][y];
7840 if (direction != MovDir[x][y])
7844 GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7846 ResetGfxFrame(x, y, FALSE);
7849 static boolean JustBeingPushed(int x, int y)
7853 for (i = 0; i < MAX_PLAYERS; i++)
7855 struct PlayerInfo *player = &stored_player[i];
7857 if (player->active && player->is_pushing && player->MovPos)
7859 int next_jx = player->jx + (player->jx - player->last_jx);
7860 int next_jy = player->jy + (player->jy - player->last_jy);
7862 if (x == next_jx && y == next_jy)
7870 void StartMoving(int x, int y)
7872 boolean started_moving = FALSE; /* some elements can fall _and_ move */
7873 int element = Feld[x][y];
7878 if (MovDelay[x][y] == 0)
7879 GfxAction[x][y] = ACTION_DEFAULT;
7881 if (CAN_FALL(element) && y < lev_fieldy - 1)
7883 if ((x > 0 && IS_PLAYER(x - 1, y)) ||
7884 (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7885 if (JustBeingPushed(x, y))
7888 if (element == EL_QUICKSAND_FULL)
7890 if (IS_FREE(x, y + 1))
7892 InitMovingField(x, y, MV_DOWN);
7893 started_moving = TRUE;
7895 Feld[x][y] = EL_QUICKSAND_EMPTYING;
7896 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7897 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7898 Store[x][y] = EL_ROCK;
7900 Store[x][y] = EL_ROCK;
7903 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7905 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7907 if (!MovDelay[x][y])
7909 MovDelay[x][y] = TILEY + 1;
7911 ResetGfxAnimation(x, y);
7912 ResetGfxAnimation(x, y + 1);
7917 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7918 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7925 Feld[x][y] = EL_QUICKSAND_EMPTY;
7926 Feld[x][y + 1] = EL_QUICKSAND_FULL;
7927 Store[x][y + 1] = Store[x][y];
7930 PlayLevelSoundAction(x, y, ACTION_FILLING);
7932 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7934 if (!MovDelay[x][y])
7936 MovDelay[x][y] = TILEY + 1;
7938 ResetGfxAnimation(x, y);
7939 ResetGfxAnimation(x, y + 1);
7944 DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7945 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7952 Feld[x][y] = EL_QUICKSAND_EMPTY;
7953 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7954 Store[x][y + 1] = Store[x][y];
7957 PlayLevelSoundAction(x, y, ACTION_FILLING);
7960 else if (element == EL_QUICKSAND_FAST_FULL)
7962 if (IS_FREE(x, y + 1))
7964 InitMovingField(x, y, MV_DOWN);
7965 started_moving = TRUE;
7967 Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7968 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7969 if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7970 Store[x][y] = EL_ROCK;
7972 Store[x][y] = EL_ROCK;
7975 PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7977 else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7979 if (!MovDelay[x][y])
7981 MovDelay[x][y] = TILEY + 1;
7983 ResetGfxAnimation(x, y);
7984 ResetGfxAnimation(x, y + 1);
7989 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7990 DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7997 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7998 Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7999 Store[x][y + 1] = Store[x][y];
8002 PlayLevelSoundAction(x, y, ACTION_FILLING);
8004 else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8006 if (!MovDelay[x][y])
8008 MovDelay[x][y] = TILEY + 1;
8010 ResetGfxAnimation(x, y);
8011 ResetGfxAnimation(x, y + 1);
8016 DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8017 DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8024 Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
8025 Feld[x][y + 1] = EL_QUICKSAND_FULL;
8026 Store[x][y + 1] = Store[x][y];
8029 PlayLevelSoundAction(x, y, ACTION_FILLING);
8032 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8033 Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
8035 InitMovingField(x, y, MV_DOWN);
8036 started_moving = TRUE;
8038 Feld[x][y] = EL_QUICKSAND_FILLING;
8039 Store[x][y] = element;
8041 PlayLevelSoundAction(x, y, ACTION_FILLING);
8043 else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8044 Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8046 InitMovingField(x, y, MV_DOWN);
8047 started_moving = TRUE;
8049 Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
8050 Store[x][y] = element;
8052 PlayLevelSoundAction(x, y, ACTION_FILLING);
8054 else if (element == EL_MAGIC_WALL_FULL)
8056 if (IS_FREE(x, y + 1))
8058 InitMovingField(x, y, MV_DOWN);
8059 started_moving = TRUE;
8061 Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
8062 Store[x][y] = EL_CHANGED(Store[x][y]);
8064 else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8066 if (!MovDelay[x][y])
8067 MovDelay[x][y] = TILEY/4 + 1;
8076 Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
8077 Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
8078 Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8082 else if (element == EL_BD_MAGIC_WALL_FULL)
8084 if (IS_FREE(x, y + 1))
8086 InitMovingField(x, y, MV_DOWN);
8087 started_moving = TRUE;
8089 Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8090 Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8092 else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8094 if (!MovDelay[x][y])
8095 MovDelay[x][y] = TILEY/4 + 1;
8104 Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8105 Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8106 Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8110 else if (element == EL_DC_MAGIC_WALL_FULL)
8112 if (IS_FREE(x, y + 1))
8114 InitMovingField(x, y, MV_DOWN);
8115 started_moving = TRUE;
8117 Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8118 Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8120 else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8122 if (!MovDelay[x][y])
8123 MovDelay[x][y] = TILEY/4 + 1;
8132 Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8133 Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8134 Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8138 else if ((CAN_PASS_MAGIC_WALL(element) &&
8139 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8140 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8141 (CAN_PASS_DC_MAGIC_WALL(element) &&
8142 (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8145 InitMovingField(x, y, MV_DOWN);
8146 started_moving = TRUE;
8149 (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8150 Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8151 EL_DC_MAGIC_WALL_FILLING);
8152 Store[x][y] = element;
8154 else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
8156 SplashAcid(x, y + 1);
8158 InitMovingField(x, y, MV_DOWN);
8159 started_moving = TRUE;
8161 Store[x][y] = EL_ACID;
8164 #if USE_FIX_IMPACT_COLLISION
8165 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8166 CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8168 (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8169 CheckCollision[x][y] && !IS_FREE(x, y + 1)) ||
8171 (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8172 CAN_FALL(element) && WasJustFalling[x][y] &&
8173 (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8175 (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8176 CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8177 (Feld[x][y + 1] == EL_BLOCKED)))
8179 /* this is needed for a special case not covered by calling "Impact()"
8180 from "ContinueMoving()": if an element moves to a tile directly below
8181 another element which was just falling on that tile (which was empty
8182 in the previous frame), the falling element above would just stop
8183 instead of smashing the element below (in previous version, the above
8184 element was just checked for "moving" instead of "falling", resulting
8185 in incorrect smashes caused by horizontal movement of the above
8186 element; also, the case of the player being the element to smash was
8187 simply not covered here... :-/ ) */
8189 CheckCollision[x][y] = 0;
8190 CheckImpact[x][y] = 0;
8194 else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8196 if (MovDir[x][y] == MV_NONE)
8198 InitMovingField(x, y, MV_DOWN);
8199 started_moving = TRUE;
8202 else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
8204 if (WasJustFalling[x][y]) /* prevent animation from being restarted */
8205 MovDir[x][y] = MV_DOWN;
8207 InitMovingField(x, y, MV_DOWN);
8208 started_moving = TRUE;
8210 else if (element == EL_AMOEBA_DROP)
8212 Feld[x][y] = EL_AMOEBA_GROWING;
8213 Store[x][y] = EL_AMOEBA_WET;
8215 else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8216 (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
8217 !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8218 element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8220 boolean can_fall_left = (x > 0 && IS_FREE(x - 1, y) &&
8221 (IS_FREE(x - 1, y + 1) ||
8222 Feld[x - 1][y + 1] == EL_ACID));
8223 boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8224 (IS_FREE(x + 1, y + 1) ||
8225 Feld[x + 1][y + 1] == EL_ACID));
8226 boolean can_fall_any = (can_fall_left || can_fall_right);
8227 boolean can_fall_both = (can_fall_left && can_fall_right);
8228 int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
8230 #if USE_NEW_ALL_SLIPPERY
8231 if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8233 if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8234 can_fall_right = FALSE;
8235 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8236 can_fall_left = FALSE;
8237 else if (slippery_type == SLIPPERY_ONLY_LEFT)
8238 can_fall_right = FALSE;
8239 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8240 can_fall_left = FALSE;
8242 can_fall_any = (can_fall_left || can_fall_right);
8243 can_fall_both = FALSE;
8246 if (can_fall_any && IS_CUSTOM_ELEMENT(Feld[x][y + 1]))
8248 if (slippery_type == SLIPPERY_ONLY_LEFT)
8249 can_fall_right = FALSE;
8250 else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8251 can_fall_left = FALSE;
8252 else if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8253 can_fall_right = FALSE;
8254 else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8255 can_fall_left = FALSE;
8257 can_fall_any = (can_fall_left || can_fall_right);
8258 can_fall_both = (can_fall_left && can_fall_right);
8262 #if USE_NEW_ALL_SLIPPERY
8264 #if USE_NEW_SP_SLIPPERY
8265 /* !!! better use the same properties as for custom elements here !!! */
8266 else if (game.engine_version >= VERSION_IDENT(3,1,1,0) &&
8267 can_fall_both && IS_SP_ELEMENT(Feld[x][y + 1]))
8269 can_fall_right = FALSE; /* slip down on left side */
8270 can_fall_both = FALSE;
8275 #if USE_NEW_ALL_SLIPPERY
8278 if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8279 can_fall_right = FALSE; /* slip down on left side */
8281 can_fall_left = !(can_fall_right = RND(2));
8283 can_fall_both = FALSE;
8288 if (game.emulation == EMU_BOULDERDASH ||
8289 element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8290 can_fall_right = FALSE; /* slip down on left side */
8292 can_fall_left = !(can_fall_right = RND(2));
8294 can_fall_both = FALSE;
8300 /* if not determined otherwise, prefer left side for slipping down */
8301 InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8302 started_moving = TRUE;
8306 else if (IS_BELT_ACTIVE(Feld[x][y + 1]) && !CAN_MOVE(element))
8308 else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
8311 boolean left_is_free = (x > 0 && IS_FREE(x - 1, y));
8312 boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8313 int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
8314 int belt_dir = game.belt_dir[belt_nr];
8316 if ((belt_dir == MV_LEFT && left_is_free) ||
8317 (belt_dir == MV_RIGHT && right_is_free))
8319 int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8321 InitMovingField(x, y, belt_dir);
8322 started_moving = TRUE;
8324 Pushed[x][y] = TRUE;
8325 Pushed[nextx][y] = TRUE;
8327 GfxAction[x][y] = ACTION_DEFAULT;
8331 MovDir[x][y] = 0; /* if element was moving, stop it */
8336 /* not "else if" because of elements that can fall and move (EL_SPRING) */
8338 if (CAN_MOVE(element) && !started_moving && MovDir[x][y] != MV_NONE)
8340 if (CAN_MOVE(element) && !started_moving)
8343 int move_pattern = element_info[element].move_pattern;
8348 if (MovDir[x][y] == MV_NONE)
8350 printf("StartMoving(): %d,%d: element %d ['%s'] not moving\n",
8351 x, y, element, element_info[element].token_name);
8352 printf("StartMoving(): This should never happen!\n");
8357 Moving2Blocked(x, y, &newx, &newy);
8359 if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8362 if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8363 CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8365 WasJustMoving[x][y] = 0;
8366 CheckCollision[x][y] = 0;
8368 TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8370 if (Feld[x][y] != element) /* element has changed */
8374 if (!MovDelay[x][y]) /* start new movement phase */
8376 /* all objects that can change their move direction after each step
8377 (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
8379 if (element != EL_YAMYAM &&
8380 element != EL_DARK_YAMYAM &&
8381 element != EL_PACMAN &&
8382 !(move_pattern & MV_ANY_DIRECTION) &&
8383 move_pattern != MV_TURNING_LEFT &&
8384 move_pattern != MV_TURNING_RIGHT &&
8385 move_pattern != MV_TURNING_LEFT_RIGHT &&
8386 move_pattern != MV_TURNING_RIGHT_LEFT &&
8387 move_pattern != MV_TURNING_RANDOM)
8391 if (MovDelay[x][y] && (element == EL_BUG ||
8392 element == EL_SPACESHIP ||
8393 element == EL_SP_SNIKSNAK ||
8394 element == EL_SP_ELECTRON ||
8395 element == EL_MOLE))
8396 TEST_DrawLevelField(x, y);
8400 if (MovDelay[x][y]) /* wait some time before next movement */
8404 if (element == EL_ROBOT ||
8405 element == EL_YAMYAM ||
8406 element == EL_DARK_YAMYAM)
8408 DrawLevelElementAnimationIfNeeded(x, y, element);
8409 PlayLevelSoundAction(x, y, ACTION_WAITING);
8411 else if (element == EL_SP_ELECTRON)
8412 DrawLevelElementAnimationIfNeeded(x, y, element);
8413 else if (element == EL_DRAGON)
8416 int dir = MovDir[x][y];
8417 int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8418 int dy = (dir == MV_UP ? -1 : dir == MV_DOWN ? +1 : 0);
8419 int graphic = (dir == MV_LEFT ? IMG_FLAMES_1_LEFT :
8420 dir == MV_RIGHT ? IMG_FLAMES_1_RIGHT :
8421 dir == MV_UP ? IMG_FLAMES_1_UP :
8422 dir == MV_DOWN ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8423 int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8425 GfxAction[x][y] = ACTION_ATTACKING;
8427 if (IS_PLAYER(x, y))
8428 DrawPlayerField(x, y);
8430 TEST_DrawLevelField(x, y);
8432 PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8434 for (i = 1; i <= 3; i++)
8436 int xx = x + i * dx;
8437 int yy = y + i * dy;
8438 int sx = SCREENX(xx);
8439 int sy = SCREENY(yy);
8440 int flame_graphic = graphic + (i - 1);
8442 if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
8447 int flamed = MovingOrBlocked2Element(xx, yy);
8451 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8453 else if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
8454 RemoveMovingField(xx, yy);
8456 RemoveField(xx, yy);
8458 if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8461 RemoveMovingField(xx, yy);
8464 ChangeDelay[xx][yy] = 0;
8466 Feld[xx][yy] = EL_FLAMES;
8468 if (IN_SCR_FIELD(sx, sy))
8470 TEST_DrawLevelFieldCrumbledSand(xx, yy);
8471 DrawGraphic(sx, sy, flame_graphic, frame);
8476 if (Feld[xx][yy] == EL_FLAMES)
8477 Feld[xx][yy] = EL_EMPTY;
8478 TEST_DrawLevelField(xx, yy);
8483 if (MovDelay[x][y]) /* element still has to wait some time */
8485 PlayLevelSoundAction(x, y, ACTION_WAITING);
8491 /* now make next step */
8493 Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
8495 if (DONT_COLLIDE_WITH(element) &&
8496 IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8497 !PLAYER_ENEMY_PROTECTED(newx, newy))
8499 TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8504 else if (CAN_MOVE_INTO_ACID(element) &&
8505 IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
8506 !IS_MV_DIAGONAL(MovDir[x][y]) &&
8507 (MovDir[x][y] == MV_DOWN ||
8508 game.engine_version >= VERSION_IDENT(3,1,0,0)))
8510 SplashAcid(newx, newy);
8511 Store[x][y] = EL_ACID;
8513 else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8515 if (Feld[newx][newy] == EL_EXIT_OPEN ||
8516 Feld[newx][newy] == EL_EM_EXIT_OPEN ||
8517 Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
8518 Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8521 TEST_DrawLevelField(x, y);
8523 PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8524 if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8525 DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8527 local_player->friends_still_needed--;
8528 if (!local_player->friends_still_needed &&
8529 !local_player->GameOver && AllPlayersGone)
8530 PlayerWins(local_player);
8534 else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
8536 if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8537 TEST_DrawLevelField(newx, newy);
8539 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8541 else if (!IS_FREE(newx, newy))
8543 GfxAction[x][y] = ACTION_WAITING;
8545 if (IS_PLAYER(x, y))
8546 DrawPlayerField(x, y);
8548 TEST_DrawLevelField(x, y);
8553 else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8555 if (IS_FOOD_PIG(Feld[newx][newy]))
8557 if (IS_MOVING(newx, newy))
8558 RemoveMovingField(newx, newy);
8561 Feld[newx][newy] = EL_EMPTY;
8562 TEST_DrawLevelField(newx, newy);
8565 PlayLevelSound(x, y, SND_PIG_DIGGING);
8567 else if (!IS_FREE(newx, newy))
8569 if (IS_PLAYER(x, y))
8570 DrawPlayerField(x, y);
8572 TEST_DrawLevelField(x, y);
8577 else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8579 if (Store[x][y] != EL_EMPTY)
8581 boolean can_clone = FALSE;
8584 /* check if element to clone is still there */
8585 for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8587 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
8595 /* cannot clone or target field not free anymore -- do not clone */
8596 if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8597 Store[x][y] = EL_EMPTY;
8600 if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8602 if (IS_MV_DIAGONAL(MovDir[x][y]))
8604 int diagonal_move_dir = MovDir[x][y];
8605 int stored = Store[x][y];
8606 int change_delay = 8;
8609 /* android is moving diagonally */
8611 CreateField(x, y, EL_DIAGONAL_SHRINKING);
8613 Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8614 GfxElement[x][y] = EL_EMC_ANDROID;
8615 GfxAction[x][y] = ACTION_SHRINKING;
8616 GfxDir[x][y] = diagonal_move_dir;
8617 ChangeDelay[x][y] = change_delay;
8619 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8622 DrawLevelGraphicAnimation(x, y, graphic);
8623 PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8625 if (Feld[newx][newy] == EL_ACID)
8627 SplashAcid(newx, newy);
8632 CreateField(newx, newy, EL_DIAGONAL_GROWING);
8634 Store[newx][newy] = EL_EMC_ANDROID;
8635 GfxElement[newx][newy] = EL_EMC_ANDROID;
8636 GfxAction[newx][newy] = ACTION_GROWING;
8637 GfxDir[newx][newy] = diagonal_move_dir;
8638 ChangeDelay[newx][newy] = change_delay;
8640 graphic = el_act_dir2img(GfxElement[newx][newy],
8641 GfxAction[newx][newy], GfxDir[newx][newy]);
8643 DrawLevelGraphicAnimation(newx, newy, graphic);
8644 PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8650 Feld[newx][newy] = EL_EMPTY;
8651 TEST_DrawLevelField(newx, newy);
8653 PlayLevelSoundAction(x, y, ACTION_DIGGING);
8656 else if (!IS_FREE(newx, newy))
8659 if (IS_PLAYER(x, y))
8660 DrawPlayerField(x, y);
8662 TEST_DrawLevelField(x, y);
8668 else if (IS_CUSTOM_ELEMENT(element) &&
8669 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8672 if (!DigFieldByCE(newx, newy, element))
8675 int new_element = Feld[newx][newy];
8677 if (!IS_FREE(newx, newy))
8679 int action = (IS_DIGGABLE(new_element) ? ACTION_DIGGING :
8680 IS_COLLECTIBLE(new_element) ? ACTION_COLLECTING :
8683 /* no element can dig solid indestructible elements */
8684 if (IS_INDESTRUCTIBLE(new_element) &&
8685 !IS_DIGGABLE(new_element) &&
8686 !IS_COLLECTIBLE(new_element))
8689 if (AmoebaNr[newx][newy] &&
8690 (new_element == EL_AMOEBA_FULL ||
8691 new_element == EL_BD_AMOEBA ||
8692 new_element == EL_AMOEBA_GROWING))
8694 AmoebaCnt[AmoebaNr[newx][newy]]--;
8695 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8698 if (IS_MOVING(newx, newy))
8699 RemoveMovingField(newx, newy);
8702 RemoveField(newx, newy);
8703 TEST_DrawLevelField(newx, newy);
8706 /* if digged element was about to explode, prevent the explosion */
8707 ExplodeField[newx][newy] = EX_TYPE_NONE;
8709 PlayLevelSoundAction(x, y, action);
8712 Store[newx][newy] = EL_EMPTY;
8715 /* this makes it possible to leave the removed element again */
8716 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8717 Store[newx][newy] = new_element;
8719 if (IS_EQUAL_OR_IN_GROUP(new_element, MOVE_ENTER_EL(element)))
8721 int move_leave_element = element_info[element].move_leave_element;
8723 /* this makes it possible to leave the removed element again */
8724 Store[newx][newy] = (move_leave_element == EL_TRIGGER_ELEMENT ?
8725 new_element : move_leave_element);
8731 if (move_pattern & MV_MAZE_RUNNER_STYLE)
8733 RunnerVisit[x][y] = FrameCounter;
8734 PlayerVisit[x][y] /= 8; /* expire player visit path */
8737 else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8739 if (!IS_FREE(newx, newy))
8741 if (IS_PLAYER(x, y))
8742 DrawPlayerField(x, y);
8744 TEST_DrawLevelField(x, y);
8750 boolean wanna_flame = !RND(10);
8751 int dx = newx - x, dy = newy - y;
8752 int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8753 int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8754 int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8755 MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8756 int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8757 MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8760 IS_CLASSIC_ENEMY(element1) ||
8761 IS_CLASSIC_ENEMY(element2)) &&
8762 element1 != EL_DRAGON && element2 != EL_DRAGON &&
8763 element1 != EL_FLAMES && element2 != EL_FLAMES)
8765 ResetGfxAnimation(x, y);
8766 GfxAction[x][y] = ACTION_ATTACKING;
8768 if (IS_PLAYER(x, y))
8769 DrawPlayerField(x, y);
8771 TEST_DrawLevelField(x, y);
8773 PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8775 MovDelay[x][y] = 50;
8779 RemoveField(newx, newy);
8781 Feld[newx][newy] = EL_FLAMES;
8782 if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
8785 RemoveField(newx1, newy1);
8787 Feld[newx1][newy1] = EL_FLAMES;
8789 if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
8792 RemoveField(newx2, newy2);
8794 Feld[newx2][newy2] = EL_FLAMES;
8801 else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8802 Feld[newx][newy] == EL_DIAMOND)
8804 if (IS_MOVING(newx, newy))
8805 RemoveMovingField(newx, newy);
8808 Feld[newx][newy] = EL_EMPTY;
8809 TEST_DrawLevelField(newx, newy);
8812 PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8814 else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8815 IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
8817 if (AmoebaNr[newx][newy])
8819 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8820 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8821 Feld[newx][newy] == EL_BD_AMOEBA)
8822 AmoebaCnt[AmoebaNr[newx][newy]]--;
8827 if (IS_MOVING(newx, newy) || IS_BLOCKED(newx, newy))
8829 RemoveMovingField(newx, newy);
8832 if (IS_MOVING(newx, newy))
8834 RemoveMovingField(newx, newy);
8839 Feld[newx][newy] = EL_EMPTY;
8840 TEST_DrawLevelField(newx, newy);
8843 PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8845 else if ((element == EL_PACMAN || element == EL_MOLE)
8846 && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
8848 if (AmoebaNr[newx][newy])
8850 AmoebaCnt2[AmoebaNr[newx][newy]]--;
8851 if (Feld[newx][newy] == EL_AMOEBA_FULL ||
8852 Feld[newx][newy] == EL_BD_AMOEBA)
8853 AmoebaCnt[AmoebaNr[newx][newy]]--;
8856 if (element == EL_MOLE)
8858 Feld[newx][newy] = EL_AMOEBA_SHRINKING;
8859 PlayLevelSound(x, y, SND_MOLE_DIGGING);
8861 ResetGfxAnimation(x, y);
8862 GfxAction[x][y] = ACTION_DIGGING;
8863 TEST_DrawLevelField(x, y);
8865 MovDelay[newx][newy] = 0; /* start amoeba shrinking delay */
8867 return; /* wait for shrinking amoeba */
8869 else /* element == EL_PACMAN */
8871 Feld[newx][newy] = EL_EMPTY;
8872 TEST_DrawLevelField(newx, newy);
8873 PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8876 else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8877 (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
8878 (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8880 /* wait for shrinking amoeba to completely disappear */
8883 else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8885 /* object was running against a wall */
8890 /* !!! NEW "CE_BLOCKED" STUFF !!! -- DOES NOT WORK YET... !!! */
8891 if (move_pattern & MV_ANY_DIRECTION &&
8892 move_pattern == MovDir[x][y])
8894 int blocking_element =
8895 (IN_LEV_FIELD(newx, newy) ? Feld[newx][newy] : BorderElement);
8897 CheckElementChangeBySide(x, y, element, blocking_element, CE_BLOCKED,
8900 element = Feld[x][y]; /* element might have changed */
8904 if (GFX_ELEMENT(element) != EL_SAND) /* !!! FIX THIS (crumble) !!! */
8905 DrawLevelElementAnimation(x, y, element);
8907 if (DONT_TOUCH(element))
8908 TestIfBadThingTouchesPlayer(x, y);
8913 InitMovingField(x, y, MovDir[x][y]);
8915 PlayLevelSoundAction(x, y, ACTION_MOVING);
8919 ContinueMoving(x, y);
8922 void ContinueMoving(int x, int y)
8924 int element = Feld[x][y];
8925 struct ElementInfo *ei = &element_info[element];
8926 int direction = MovDir[x][y];
8927 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8928 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
8929 int newx = x + dx, newy = y + dy;
8930 int stored = Store[x][y];
8931 int stored_new = Store[newx][newy];
8932 boolean pushed_by_player = (Pushed[x][y] && IS_PLAYER(x, y));
8933 boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8934 boolean last_line = (newy == lev_fieldy - 1);
8936 MovPos[x][y] += getElementMoveStepsize(x, y);
8938 if (pushed_by_player) /* special case: moving object pushed by player */
8939 MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8941 if (ABS(MovPos[x][y]) < TILEX)
8944 int ee = Feld[x][y];
8945 int gg = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
8946 int ff = getGraphicAnimationFrame(gg, GfxFrame[x][y]);
8948 printf("::: %d.%d: moving %d ... [%d, %d, %d] [%d, %d, %d]\n",
8949 x, y, ABS(MovPos[x][y]),
8951 GfxAction[x][y], GfxDir[x][y], GfxFrame[x][y]);
8954 TEST_DrawLevelField(x, y);
8956 return; /* element is still moving */
8959 /* element reached destination field */
8961 Feld[x][y] = EL_EMPTY;
8962 Feld[newx][newy] = element;
8963 MovPos[x][y] = 0; /* force "not moving" for "crumbled sand" */
8965 if (Store[x][y] == EL_ACID) /* element is moving into acid pool */
8967 element = Feld[newx][newy] = EL_ACID;
8969 else if (element == EL_MOLE)
8971 Feld[x][y] = EL_SAND;
8973 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
8975 else if (element == EL_QUICKSAND_FILLING)
8977 element = Feld[newx][newy] = get_next_element(element);
8978 Store[newx][newy] = Store[x][y];
8980 else if (element == EL_QUICKSAND_EMPTYING)
8982 Feld[x][y] = get_next_element(element);
8983 element = Feld[newx][newy] = Store[x][y];
8985 else if (element == EL_QUICKSAND_FAST_FILLING)
8987 element = Feld[newx][newy] = get_next_element(element);
8988 Store[newx][newy] = Store[x][y];
8990 else if (element == EL_QUICKSAND_FAST_EMPTYING)
8992 Feld[x][y] = get_next_element(element);
8993 element = Feld[newx][newy] = Store[x][y];
8995 else if (element == EL_MAGIC_WALL_FILLING)
8997 element = Feld[newx][newy] = get_next_element(element);
8998 if (!game.magic_wall_active)
8999 element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
9000 Store[newx][newy] = Store[x][y];
9002 else if (element == EL_MAGIC_WALL_EMPTYING)
9004 Feld[x][y] = get_next_element(element);
9005 if (!game.magic_wall_active)
9006 Feld[x][y] = EL_MAGIC_WALL_DEAD;
9007 element = Feld[newx][newy] = Store[x][y];
9009 #if USE_NEW_CUSTOM_VALUE
9010 InitField(newx, newy, FALSE);
9013 else if (element == EL_BD_MAGIC_WALL_FILLING)
9015 element = Feld[newx][newy] = get_next_element(element);
9016 if (!game.magic_wall_active)
9017 element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
9018 Store[newx][newy] = Store[x][y];
9020 else if (element == EL_BD_MAGIC_WALL_EMPTYING)
9022 Feld[x][y] = get_next_element(element);
9023 if (!game.magic_wall_active)
9024 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
9025 element = Feld[newx][newy] = Store[x][y];
9027 #if USE_NEW_CUSTOM_VALUE
9028 InitField(newx, newy, FALSE);
9031 else if (element == EL_DC_MAGIC_WALL_FILLING)
9033 element = Feld[newx][newy] = get_next_element(element);
9034 if (!game.magic_wall_active)
9035 element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
9036 Store[newx][newy] = Store[x][y];
9038 else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9040 Feld[x][y] = get_next_element(element);
9041 if (!game.magic_wall_active)
9042 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
9043 element = Feld[newx][newy] = Store[x][y];
9045 #if USE_NEW_CUSTOM_VALUE
9046 InitField(newx, newy, FALSE);
9049 else if (element == EL_AMOEBA_DROPPING)
9051 Feld[x][y] = get_next_element(element);
9052 element = Feld[newx][newy] = Store[x][y];
9054 else if (element == EL_SOKOBAN_OBJECT)
9057 Feld[x][y] = Back[x][y];
9059 if (Back[newx][newy])
9060 Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9062 Back[x][y] = Back[newx][newy] = 0;
9065 Store[x][y] = EL_EMPTY;
9070 MovDelay[newx][newy] = 0;
9072 if (CAN_CHANGE_OR_HAS_ACTION(element))
9074 /* copy element change control values to new field */
9075 ChangeDelay[newx][newy] = ChangeDelay[x][y];
9076 ChangePage[newx][newy] = ChangePage[x][y];
9077 ChangeCount[newx][newy] = ChangeCount[x][y];
9078 ChangeEvent[newx][newy] = ChangeEvent[x][y];
9081 #if USE_NEW_CUSTOM_VALUE
9082 CustomValue[newx][newy] = CustomValue[x][y];
9085 ChangeDelay[x][y] = 0;
9086 ChangePage[x][y] = -1;
9087 ChangeCount[x][y] = 0;
9088 ChangeEvent[x][y] = -1;
9090 #if USE_NEW_CUSTOM_VALUE
9091 CustomValue[x][y] = 0;
9094 /* copy animation control values to new field */
9095 GfxFrame[newx][newy] = GfxFrame[x][y];
9096 GfxRandom[newx][newy] = GfxRandom[x][y]; /* keep same random value */
9097 GfxAction[newx][newy] = GfxAction[x][y]; /* keep action one frame */
9098 GfxDir[newx][newy] = GfxDir[x][y]; /* keep element direction */
9100 Pushed[x][y] = Pushed[newx][newy] = FALSE;
9102 /* some elements can leave other elements behind after moving */
9104 if (ei->move_leave_element != EL_EMPTY &&
9105 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9106 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9108 if (IS_CUSTOM_ELEMENT(element) && ei->move_leave_element != EL_EMPTY &&
9109 (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9110 (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9113 int move_leave_element = ei->move_leave_element;
9117 /* this makes it possible to leave the removed element again */
9118 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9119 move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9121 /* this makes it possible to leave the removed element again */
9122 if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9123 move_leave_element = stored;
9126 /* this makes it possible to leave the removed element again */
9127 if (ei->move_leave_type == LEAVE_TYPE_LIMITED &&
9128 ei->move_leave_element == EL_TRIGGER_ELEMENT)
9129 move_leave_element = stored;
9132 Feld[x][y] = move_leave_element;
9134 if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
9135 MovDir[x][y] = direction;
9137 InitField(x, y, FALSE);
9139 if (GFX_CRUMBLED(Feld[x][y]))
9140 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
9142 if (ELEM_IS_PLAYER(move_leave_element))
9143 RelocatePlayer(x, y, move_leave_element);
9146 /* do this after checking for left-behind element */
9147 ResetGfxAnimation(x, y); /* reset animation values for old field */
9149 if (!CAN_MOVE(element) ||
9150 (CAN_FALL(element) && direction == MV_DOWN &&
9151 (element == EL_SPRING ||
9152 element_info[element].move_pattern == MV_WHEN_PUSHED ||
9153 element_info[element].move_pattern == MV_WHEN_DROPPED)))
9154 GfxDir[x][y] = MovDir[newx][newy] = 0;
9156 TEST_DrawLevelField(x, y);
9157 TEST_DrawLevelField(newx, newy);
9159 Stop[newx][newy] = TRUE; /* ignore this element until the next frame */
9161 /* prevent pushed element from moving on in pushed direction */
9162 if (pushed_by_player && CAN_MOVE(element) &&
9163 element_info[element].move_pattern & MV_ANY_DIRECTION &&
9164 !(element_info[element].move_pattern & direction))
9165 TurnRound(newx, newy);
9167 /* prevent elements on conveyor belt from moving on in last direction */
9168 if (pushed_by_conveyor && CAN_FALL(element) &&
9169 direction & MV_HORIZONTAL)
9170 MovDir[newx][newy] = 0;
9172 if (!pushed_by_player)
9174 int nextx = newx + dx, nexty = newy + dy;
9175 boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9177 WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9179 if (CAN_FALL(element) && direction == MV_DOWN)
9180 WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9182 if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9183 CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9185 #if USE_FIX_IMPACT_COLLISION
9186 if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9187 CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9191 if (DONT_TOUCH(element)) /* object may be nasty to player or others */
9193 TestIfBadThingTouchesPlayer(newx, newy);
9194 TestIfBadThingTouchesFriend(newx, newy);
9196 if (!IS_CUSTOM_ELEMENT(element))
9197 TestIfBadThingTouchesOtherBadThing(newx, newy);
9199 else if (element == EL_PENGUIN)
9200 TestIfFriendTouchesBadThing(newx, newy);
9202 if (DONT_GET_HIT_BY(element))
9204 TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9207 /* give the player one last chance (one more frame) to move away */
9208 if (CAN_FALL(element) && direction == MV_DOWN &&
9209 (last_line || (!IS_FREE(x, newy + 1) &&
9210 (!IS_PLAYER(x, newy + 1) ||
9211 game.engine_version < VERSION_IDENT(3,1,1,0)))))
9214 if (pushed_by_player && !game.use_change_when_pushing_bug)
9216 int push_side = MV_DIR_OPPOSITE(direction);
9217 struct PlayerInfo *player = PLAYERINFO(x, y);
9219 CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9220 player->index_bit, push_side);
9221 CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
9222 player->index_bit, push_side);
9225 if (element == EL_EMC_ANDROID && pushed_by_player) /* make another move */
9226 MovDelay[newx][newy] = 1;
9228 CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9230 TestIfElementTouchesCustomElement(x, y); /* empty or new element */
9233 if (ChangePage[newx][newy] != -1) /* delayed change */
9235 int page = ChangePage[newx][newy];
9236 struct ElementChangeInfo *change = &ei->change_page[page];
9238 ChangePage[newx][newy] = -1;
9240 if (change->can_change)
9242 if (ChangeElement(newx, newy, element, page))
9244 if (change->post_change_function)
9245 change->post_change_function(newx, newy);
9249 if (change->has_action)
9250 ExecuteCustomElementAction(newx, newy, element, page);
9254 TestIfElementHitsCustomElement(newx, newy, direction);
9255 TestIfPlayerTouchesCustomElement(newx, newy);
9256 TestIfElementTouchesCustomElement(newx, newy);
9258 if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9259 IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9260 CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9261 MV_DIR_OPPOSITE(direction));
9264 int AmoebeNachbarNr(int ax, int ay)
9267 int element = Feld[ax][ay];
9269 static int xy[4][2] =
9277 for (i = 0; i < NUM_DIRECTIONS; i++)
9279 int x = ax + xy[i][0];
9280 int y = ay + xy[i][1];
9282 if (!IN_LEV_FIELD(x, y))
9285 if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
9286 group_nr = AmoebaNr[x][y];
9292 void AmoebenVereinigen(int ax, int ay)
9294 int i, x, y, xx, yy;
9295 int new_group_nr = AmoebaNr[ax][ay];
9296 static int xy[4][2] =
9304 if (new_group_nr == 0)
9307 for (i = 0; i < NUM_DIRECTIONS; i++)
9312 if (!IN_LEV_FIELD(x, y))
9315 if ((Feld[x][y] == EL_AMOEBA_FULL ||
9316 Feld[x][y] == EL_BD_AMOEBA ||
9317 Feld[x][y] == EL_AMOEBA_DEAD) &&
9318 AmoebaNr[x][y] != new_group_nr)
9320 int old_group_nr = AmoebaNr[x][y];
9322 if (old_group_nr == 0)
9325 AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9326 AmoebaCnt[old_group_nr] = 0;
9327 AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9328 AmoebaCnt2[old_group_nr] = 0;
9330 SCAN_PLAYFIELD(xx, yy)
9332 if (AmoebaNr[xx][yy] == old_group_nr)
9333 AmoebaNr[xx][yy] = new_group_nr;
9339 void AmoebeUmwandeln(int ax, int ay)
9343 if (Feld[ax][ay] == EL_AMOEBA_DEAD)
9345 int group_nr = AmoebaNr[ax][ay];
9350 printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
9351 printf("AmoebeUmwandeln(): This should never happen!\n");
9356 SCAN_PLAYFIELD(x, y)
9358 if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9361 Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
9365 PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9366 SND_AMOEBA_TURNING_TO_GEM :
9367 SND_AMOEBA_TURNING_TO_ROCK));
9372 static int xy[4][2] =
9380 for (i = 0; i < NUM_DIRECTIONS; i++)
9385 if (!IN_LEV_FIELD(x, y))
9388 if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
9390 PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9391 SND_AMOEBA_TURNING_TO_GEM :
9392 SND_AMOEBA_TURNING_TO_ROCK));
9399 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
9402 int group_nr = AmoebaNr[ax][ay];
9403 boolean done = FALSE;
9408 printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
9409 printf("AmoebeUmwandelnBD(): This should never happen!\n");
9414 SCAN_PLAYFIELD(x, y)
9416 if (AmoebaNr[x][y] == group_nr &&
9417 (Feld[x][y] == EL_AMOEBA_DEAD ||
9418 Feld[x][y] == EL_BD_AMOEBA ||
9419 Feld[x][y] == EL_AMOEBA_GROWING))
9422 Feld[x][y] = new_element;
9423 InitField(x, y, FALSE);
9424 TEST_DrawLevelField(x, y);
9430 PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9431 SND_BD_AMOEBA_TURNING_TO_ROCK :
9432 SND_BD_AMOEBA_TURNING_TO_GEM));
9435 void AmoebeWaechst(int x, int y)
9437 static unsigned long sound_delay = 0;
9438 static unsigned long sound_delay_value = 0;
9440 if (!MovDelay[x][y]) /* start new growing cycle */
9444 if (DelayReached(&sound_delay, sound_delay_value))
9446 PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9447 sound_delay_value = 30;
9451 if (MovDelay[x][y]) /* wait some time before growing bigger */
9454 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9456 int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9457 6 - MovDelay[x][y]);
9459 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9462 if (!MovDelay[x][y])
9464 Feld[x][y] = Store[x][y];
9466 TEST_DrawLevelField(x, y);
9471 void AmoebaDisappearing(int x, int y)
9473 static unsigned long sound_delay = 0;
9474 static unsigned long sound_delay_value = 0;
9476 if (!MovDelay[x][y]) /* start new shrinking cycle */
9480 if (DelayReached(&sound_delay, sound_delay_value))
9481 sound_delay_value = 30;
9484 if (MovDelay[x][y]) /* wait some time before shrinking */
9487 if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9489 int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9490 6 - MovDelay[x][y]);
9492 DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9495 if (!MovDelay[x][y])
9497 Feld[x][y] = EL_EMPTY;
9498 TEST_DrawLevelField(x, y);
9500 /* don't let mole enter this field in this cycle;
9501 (give priority to objects falling to this field from above) */
9507 void AmoebeAbleger(int ax, int ay)
9510 int element = Feld[ax][ay];
9511 int graphic = el2img(element);
9512 int newax = ax, neway = ay;
9513 boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9514 static int xy[4][2] =
9522 if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9524 Feld[ax][ay] = EL_AMOEBA_DEAD;
9525 TEST_DrawLevelField(ax, ay);
9529 if (IS_ANIMATED(graphic))
9530 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9532 if (!MovDelay[ax][ay]) /* start making new amoeba field */
9533 MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9535 if (MovDelay[ax][ay]) /* wait some time before making new amoeba */
9538 if (MovDelay[ax][ay])
9542 if (can_drop) /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
9545 int x = ax + xy[start][0];
9546 int y = ay + xy[start][1];
9548 if (!IN_LEV_FIELD(x, y))
9551 if (IS_FREE(x, y) ||
9552 CAN_GROW_INTO(Feld[x][y]) ||
9553 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9554 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9560 if (newax == ax && neway == ay)
9563 else /* normal or "filled" (BD style) amoeba */
9566 boolean waiting_for_player = FALSE;
9568 for (i = 0; i < NUM_DIRECTIONS; i++)
9570 int j = (start + i) % 4;
9571 int x = ax + xy[j][0];
9572 int y = ay + xy[j][1];
9574 if (!IN_LEV_FIELD(x, y))
9577 if (IS_FREE(x, y) ||
9578 CAN_GROW_INTO(Feld[x][y]) ||
9579 Feld[x][y] == EL_QUICKSAND_EMPTY ||
9580 Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
9586 else if (IS_PLAYER(x, y))
9587 waiting_for_player = TRUE;
9590 if (newax == ax && neway == ay) /* amoeba cannot grow */
9592 if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9594 Feld[ax][ay] = EL_AMOEBA_DEAD;
9595 TEST_DrawLevelField(ax, ay);
9596 AmoebaCnt[AmoebaNr[ax][ay]]--;
9598 if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0) /* amoeba is completely dead */
9600 if (element == EL_AMOEBA_FULL)
9601 AmoebeUmwandeln(ax, ay);
9602 else if (element == EL_BD_AMOEBA)
9603 AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
9608 else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9610 /* amoeba gets larger by growing in some direction */
9612 int new_group_nr = AmoebaNr[ax][ay];
9615 if (new_group_nr == 0)
9617 printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
9618 printf("AmoebeAbleger(): This should never happen!\n");
9623 AmoebaNr[newax][neway] = new_group_nr;
9624 AmoebaCnt[new_group_nr]++;
9625 AmoebaCnt2[new_group_nr]++;
9627 /* if amoeba touches other amoeba(s) after growing, unify them */
9628 AmoebenVereinigen(newax, neway);
9630 if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9632 AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
9638 if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9639 (neway == lev_fieldy - 1 && newax != ax))
9641 Feld[newax][neway] = EL_AMOEBA_GROWING; /* creation of new amoeba */
9642 Store[newax][neway] = element;
9644 else if (neway == ay || element == EL_EMC_DRIPPER)
9646 Feld[newax][neway] = EL_AMOEBA_DROP; /* drop left/right of amoeba */
9648 PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9652 InitMovingField(ax, ay, MV_DOWN); /* drop dripping from amoeba */
9653 Feld[ax][ay] = EL_AMOEBA_DROPPING;
9654 Store[ax][ay] = EL_AMOEBA_DROP;
9655 ContinueMoving(ax, ay);
9659 TEST_DrawLevelField(newax, neway);
9662 void Life(int ax, int ay)
9666 int element = Feld[ax][ay];
9667 int graphic = el2img(element);
9668 int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9670 boolean changed = FALSE;
9672 if (IS_ANIMATED(graphic))
9673 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9678 if (!MovDelay[ax][ay]) /* start new "game of life" cycle */
9679 MovDelay[ax][ay] = life_time;
9681 if (MovDelay[ax][ay]) /* wait some time before next cycle */
9684 if (MovDelay[ax][ay])
9688 for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9690 int xx = ax+x1, yy = ay+y1;
9693 if (!IN_LEV_FIELD(xx, yy))
9696 for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9698 int x = xx+x2, y = yy+y2;
9700 if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9703 if (((Feld[x][y] == element ||
9704 (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
9706 (IS_FREE(x, y) && Stop[x][y]))
9710 if (xx == ax && yy == ay) /* field in the middle */
9712 if (nachbarn < life_parameter[0] ||
9713 nachbarn > life_parameter[1])
9715 Feld[xx][yy] = EL_EMPTY;
9717 TEST_DrawLevelField(xx, yy);
9718 Stop[xx][yy] = TRUE;
9722 else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
9723 { /* free border field */
9724 if (nachbarn >= life_parameter[2] &&
9725 nachbarn <= life_parameter[3])
9727 Feld[xx][yy] = element;
9728 MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9730 TEST_DrawLevelField(xx, yy);
9731 Stop[xx][yy] = TRUE;
9738 PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9739 SND_GAME_OF_LIFE_GROWING);
9742 static void InitRobotWheel(int x, int y)
9744 ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9747 static void RunRobotWheel(int x, int y)
9749 PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9752 static void StopRobotWheel(int x, int y)
9754 if (ZX == x && ZY == y)
9758 game.robot_wheel_active = FALSE;
9762 static void InitTimegateWheel(int x, int y)
9764 ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9767 static void RunTimegateWheel(int x, int y)
9769 PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9772 static void InitMagicBallDelay(int x, int y)
9775 ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9777 ChangeDelay[x][y] = level.ball_time * FRAMES_PER_SECOND + 1;
9781 static void ActivateMagicBall(int bx, int by)
9785 if (level.ball_random)
9787 int pos_border = RND(8); /* select one of the eight border elements */
9788 int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9789 int xx = pos_content % 3;
9790 int yy = pos_content / 3;
9795 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9796 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9800 for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9802 int xx = x - bx + 1;
9803 int yy = y - by + 1;
9805 if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
9806 CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9810 game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9813 void CheckExit(int x, int y)
9815 if (local_player->gems_still_needed > 0 ||
9816 local_player->sokobanfields_still_needed > 0 ||
9817 local_player->lights_still_needed > 0)
9819 int element = Feld[x][y];
9820 int graphic = el2img(element);
9822 if (IS_ANIMATED(graphic))
9823 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9828 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9831 Feld[x][y] = EL_EXIT_OPENING;
9833 PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9836 void CheckExitEM(int x, int y)
9838 if (local_player->gems_still_needed > 0 ||
9839 local_player->sokobanfields_still_needed > 0 ||
9840 local_player->lights_still_needed > 0)
9842 int element = Feld[x][y];
9843 int graphic = el2img(element);
9845 if (IS_ANIMATED(graphic))
9846 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9851 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9854 Feld[x][y] = EL_EM_EXIT_OPENING;
9856 PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9859 void CheckExitSteel(int x, int y)
9861 if (local_player->gems_still_needed > 0 ||
9862 local_player->sokobanfields_still_needed > 0 ||
9863 local_player->lights_still_needed > 0)
9865 int element = Feld[x][y];
9866 int graphic = el2img(element);
9868 if (IS_ANIMATED(graphic))
9869 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9874 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9877 Feld[x][y] = EL_STEEL_EXIT_OPENING;
9879 PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9882 void CheckExitSteelEM(int x, int y)
9884 if (local_player->gems_still_needed > 0 ||
9885 local_player->sokobanfields_still_needed > 0 ||
9886 local_player->lights_still_needed > 0)
9888 int element = Feld[x][y];
9889 int graphic = el2img(element);
9891 if (IS_ANIMATED(graphic))
9892 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9897 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9900 Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
9902 PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9905 void CheckExitSP(int x, int y)
9907 if (local_player->gems_still_needed > 0)
9909 int element = Feld[x][y];
9910 int graphic = el2img(element);
9912 if (IS_ANIMATED(graphic))
9913 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9918 if (AllPlayersGone) /* do not re-open exit door closed after last player */
9921 Feld[x][y] = EL_SP_EXIT_OPENING;
9923 PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9926 static void CloseAllOpenTimegates()
9930 SCAN_PLAYFIELD(x, y)
9932 int element = Feld[x][y];
9934 if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9936 Feld[x][y] = EL_TIMEGATE_CLOSING;
9938 PlayLevelSoundAction(x, y, ACTION_CLOSING);
9943 void DrawTwinkleOnField(int x, int y)
9945 if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9948 if (Feld[x][y] == EL_BD_DIAMOND)
9951 if (MovDelay[x][y] == 0) /* next animation frame */
9952 MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9954 if (MovDelay[x][y] != 0) /* wait some time before next frame */
9958 DrawLevelElementAnimation(x, y, Feld[x][y]);
9960 if (MovDelay[x][y] != 0)
9962 int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9963 10 - MovDelay[x][y]);
9965 DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9970 void MauerWaechst(int x, int y)
9974 if (!MovDelay[x][y]) /* next animation frame */
9975 MovDelay[x][y] = 3 * delay;
9977 if (MovDelay[x][y]) /* wait some time before next frame */
9981 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9983 int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9984 int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9986 DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9989 if (!MovDelay[x][y])
9991 if (MovDir[x][y] == MV_LEFT)
9993 if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9994 TEST_DrawLevelField(x - 1, y);
9996 else if (MovDir[x][y] == MV_RIGHT)
9998 if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9999 TEST_DrawLevelField(x + 1, y);
10001 else if (MovDir[x][y] == MV_UP)
10003 if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
10004 TEST_DrawLevelField(x, y - 1);
10008 if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
10009 TEST_DrawLevelField(x, y + 1);
10012 Feld[x][y] = Store[x][y];
10014 GfxDir[x][y] = MovDir[x][y] = MV_NONE;
10015 TEST_DrawLevelField(x, y);
10020 void MauerAbleger(int ax, int ay)
10022 int element = Feld[ax][ay];
10023 int graphic = el2img(element);
10024 boolean oben_frei = FALSE, unten_frei = FALSE;
10025 boolean links_frei = FALSE, rechts_frei = FALSE;
10026 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10027 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10028 boolean new_wall = FALSE;
10030 if (IS_ANIMATED(graphic))
10031 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10033 if (!MovDelay[ax][ay]) /* start building new wall */
10034 MovDelay[ax][ay] = 6;
10036 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10038 MovDelay[ax][ay]--;
10039 if (MovDelay[ax][ay])
10043 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10045 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10047 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10049 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10050 rechts_frei = TRUE;
10052 if (element == EL_EXPANDABLE_WALL_VERTICAL ||
10053 element == EL_EXPANDABLE_WALL_ANY)
10057 Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
10058 Store[ax][ay-1] = element;
10059 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10060 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10061 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10062 IMG_EXPANDABLE_WALL_GROWING_UP, 0);
10067 Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
10068 Store[ax][ay+1] = element;
10069 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10070 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10071 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10072 IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
10077 if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10078 element == EL_EXPANDABLE_WALL_ANY ||
10079 element == EL_EXPANDABLE_WALL ||
10080 element == EL_BD_EXPANDABLE_WALL)
10084 Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
10085 Store[ax-1][ay] = element;
10086 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10087 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10088 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10089 IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
10095 Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
10096 Store[ax+1][ay] = element;
10097 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10098 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10099 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10100 IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
10105 if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
10106 TEST_DrawLevelField(ax, ay);
10108 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10109 oben_massiv = TRUE;
10110 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10111 unten_massiv = TRUE;
10112 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10113 links_massiv = TRUE;
10114 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10115 rechts_massiv = TRUE;
10117 if (((oben_massiv && unten_massiv) ||
10118 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
10119 element == EL_EXPANDABLE_WALL) &&
10120 ((links_massiv && rechts_massiv) ||
10121 element == EL_EXPANDABLE_WALL_VERTICAL))
10122 Feld[ax][ay] = EL_WALL;
10125 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10128 void MauerAblegerStahl(int ax, int ay)
10130 int element = Feld[ax][ay];
10131 int graphic = el2img(element);
10132 boolean oben_frei = FALSE, unten_frei = FALSE;
10133 boolean links_frei = FALSE, rechts_frei = FALSE;
10134 boolean oben_massiv = FALSE, unten_massiv = FALSE;
10135 boolean links_massiv = FALSE, rechts_massiv = FALSE;
10136 boolean new_wall = FALSE;
10138 if (IS_ANIMATED(graphic))
10139 DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
10141 if (!MovDelay[ax][ay]) /* start building new wall */
10142 MovDelay[ax][ay] = 6;
10144 if (MovDelay[ax][ay]) /* wait some time before building new wall */
10146 MovDelay[ax][ay]--;
10147 if (MovDelay[ax][ay])
10151 if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
10153 if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
10155 if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
10157 if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
10158 rechts_frei = TRUE;
10160 if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
10161 element == EL_EXPANDABLE_STEELWALL_ANY)
10165 Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
10166 Store[ax][ay-1] = element;
10167 GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
10168 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
10169 DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
10170 IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
10175 Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
10176 Store[ax][ay+1] = element;
10177 GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
10178 if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
10179 DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
10180 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
10185 if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
10186 element == EL_EXPANDABLE_STEELWALL_ANY)
10190 Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10191 Store[ax-1][ay] = element;
10192 GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
10193 if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
10194 DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
10195 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
10201 Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
10202 Store[ax+1][ay] = element;
10203 GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
10204 if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
10205 DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
10206 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
10211 if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
10212 oben_massiv = TRUE;
10213 if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
10214 unten_massiv = TRUE;
10215 if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
10216 links_massiv = TRUE;
10217 if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
10218 rechts_massiv = TRUE;
10220 if (((oben_massiv && unten_massiv) ||
10221 element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
10222 ((links_massiv && rechts_massiv) ||
10223 element == EL_EXPANDABLE_STEELWALL_VERTICAL))
10224 Feld[ax][ay] = EL_STEELWALL;
10227 PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10230 void CheckForDragon(int x, int y)
10233 boolean dragon_found = FALSE;
10234 static int xy[4][2] =
10242 for (i = 0; i < NUM_DIRECTIONS; i++)
10244 for (j = 0; j < 4; j++)
10246 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10248 if (IN_LEV_FIELD(xx, yy) &&
10249 (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
10251 if (Feld[xx][yy] == EL_DRAGON)
10252 dragon_found = TRUE;
10261 for (i = 0; i < NUM_DIRECTIONS; i++)
10263 for (j = 0; j < 3; j++)
10265 int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
10267 if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
10269 Feld[xx][yy] = EL_EMPTY;
10270 TEST_DrawLevelField(xx, yy);
10279 static void InitBuggyBase(int x, int y)
10281 int element = Feld[x][y];
10282 int activating_delay = FRAMES_PER_SECOND / 4;
10284 ChangeDelay[x][y] =
10285 (element == EL_SP_BUGGY_BASE ?
10286 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10287 element == EL_SP_BUGGY_BASE_ACTIVATING ?
10289 element == EL_SP_BUGGY_BASE_ACTIVE ?
10290 1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10293 static void WarnBuggyBase(int x, int y)
10296 static int xy[4][2] =
10304 for (i = 0; i < NUM_DIRECTIONS; i++)
10306 int xx = x + xy[i][0];
10307 int yy = y + xy[i][1];
10309 if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10311 PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10318 static void InitTrap(int x, int y)
10320 ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10323 static void ActivateTrap(int x, int y)
10325 PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10328 static void ChangeActiveTrap(int x, int y)
10330 int graphic = IMG_TRAP_ACTIVE;
10332 /* if new animation frame was drawn, correct crumbled sand border */
10333 if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10334 TEST_DrawLevelFieldCrumbledSand(x, y);
10337 static int getSpecialActionElement(int element, int number, int base_element)
10339 return (element != EL_EMPTY ? element :
10340 number != -1 ? base_element + number - 1 :
10344 static int getModifiedActionNumber(int value_old, int operator, int operand,
10345 int value_min, int value_max)
10347 int value_new = (operator == CA_MODE_SET ? operand :
10348 operator == CA_MODE_ADD ? value_old + operand :
10349 operator == CA_MODE_SUBTRACT ? value_old - operand :
10350 operator == CA_MODE_MULTIPLY ? value_old * operand :
10351 operator == CA_MODE_DIVIDE ? value_old / MAX(1, operand) :
10352 operator == CA_MODE_MODULO ? value_old % MAX(1, operand) :
10355 return (value_new < value_min ? value_min :
10356 value_new > value_max ? value_max :
10360 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10362 struct ElementInfo *ei = &element_info[element];
10363 struct ElementChangeInfo *change = &ei->change_page[page];
10364 int target_element = change->target_element;
10365 int action_type = change->action_type;
10366 int action_mode = change->action_mode;
10367 int action_arg = change->action_arg;
10368 int action_element = change->action_element;
10371 if (!change->has_action)
10374 /* ---------- determine action paramater values -------------------------- */
10376 int level_time_value =
10377 (level.time > 0 ? TimeLeft :
10380 int action_arg_element_raw =
10381 (action_arg == CA_ARG_PLAYER_TRIGGER ? change->actual_trigger_player :
10382 action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10383 action_arg == CA_ARG_ELEMENT_TARGET ? change->target_element :
10384 action_arg == CA_ARG_ELEMENT_ACTION ? change->action_element :
10385 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10386 action_arg == CA_ARG_INVENTORY_RM_TARGET ? change->target_element :
10387 action_arg == CA_ARG_INVENTORY_RM_ACTION ? change->action_element :
10389 int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10392 if (action_arg_element_raw == EL_GROUP_START)
10393 printf("::: %d,%d: %d ('%s')\n", x, y, element, EL_NAME(element));
10396 int action_arg_direction =
10397 (action_arg >= CA_ARG_DIRECTION_LEFT &&
10398 action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10399 action_arg == CA_ARG_DIRECTION_TRIGGER ?
10400 change->actual_trigger_side :
10401 action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10402 MV_DIR_OPPOSITE(change->actual_trigger_side) :
10405 int action_arg_number_min =
10406 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10409 int action_arg_number_max =
10410 (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10411 action_type == CA_SET_LEVEL_GEMS ? 999 :
10412 action_type == CA_SET_LEVEL_TIME ? 9999 :
10413 action_type == CA_SET_LEVEL_SCORE ? 99999 :
10414 action_type == CA_SET_CE_VALUE ? 9999 :
10415 action_type == CA_SET_CE_SCORE ? 9999 :
10418 int action_arg_number_reset =
10419 (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10420 action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10421 action_type == CA_SET_LEVEL_TIME ? level.time :
10422 action_type == CA_SET_LEVEL_SCORE ? 0 :
10423 #if USE_NEW_CUSTOM_VALUE
10424 action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10426 action_type == CA_SET_CE_VALUE ? ei->custom_value_initial :
10428 action_type == CA_SET_CE_SCORE ? 0 :
10431 int action_arg_number =
10432 (action_arg <= CA_ARG_MAX ? action_arg :
10433 action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10434 action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10435 action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10436 action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10437 action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10438 action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10439 #if USE_NEW_CUSTOM_VALUE
10440 action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10442 action_arg == CA_ARG_NUMBER_CE_VALUE ? ei->custom_value_initial :
10444 action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10445 action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10446 action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10447 action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
10448 action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
10449 action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10450 action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10451 action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10452 action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10453 action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10454 action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10455 action_arg == CA_ARG_ELEMENT_NR_TARGET ? change->target_element :
10456 action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10457 action_arg == CA_ARG_ELEMENT_NR_ACTION ? change->action_element :
10460 int action_arg_number_old =
10461 (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
10462 action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10463 action_type == CA_SET_LEVEL_SCORE ? local_player->score :
10464 action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10465 action_type == CA_SET_CE_SCORE ? ei->collect_score :
10468 int action_arg_number_new =
10469 getModifiedActionNumber(action_arg_number_old,
10470 action_mode, action_arg_number,
10471 action_arg_number_min, action_arg_number_max);
10474 int trigger_player_bits =
10475 (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10476 change->actual_trigger_player_bits : change->trigger_player);
10478 int trigger_player_bits =
10479 (change->actual_trigger_player >= EL_PLAYER_1 &&
10480 change->actual_trigger_player <= EL_PLAYER_4 ?
10481 (1 << (change->actual_trigger_player - EL_PLAYER_1)) :
10485 int action_arg_player_bits =
10486 (action_arg >= CA_ARG_PLAYER_1 &&
10487 action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10488 action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10489 action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10492 /* ---------- execute action -------------------------------------------- */
10494 switch (action_type)
10501 /* ---------- level actions ------------------------------------------- */
10503 case CA_RESTART_LEVEL:
10505 game.restart_level = TRUE;
10510 case CA_SHOW_ENVELOPE:
10512 int element = getSpecialActionElement(action_arg_element,
10513 action_arg_number, EL_ENVELOPE_1);
10515 if (IS_ENVELOPE(element))
10516 local_player->show_envelope = element;
10521 case CA_SET_LEVEL_TIME:
10523 if (level.time > 0) /* only modify limited time value */
10525 TimeLeft = action_arg_number_new;
10528 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10530 DisplayGameControlValues();
10532 DrawGameValue_Time(TimeLeft);
10535 if (!TimeLeft && setup.time_limit)
10536 for (i = 0; i < MAX_PLAYERS; i++)
10537 KillPlayer(&stored_player[i]);
10543 case CA_SET_LEVEL_SCORE:
10545 local_player->score = action_arg_number_new;
10548 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
10550 DisplayGameControlValues();
10552 DrawGameValue_Score(local_player->score);
10558 case CA_SET_LEVEL_GEMS:
10560 local_player->gems_still_needed = action_arg_number_new;
10563 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
10565 DisplayGameControlValues();
10567 DrawGameValue_Emeralds(local_player->gems_still_needed);
10573 #if !USE_PLAYER_GRAVITY
10574 case CA_SET_LEVEL_GRAVITY:
10576 game.gravity = (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10577 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10578 action_arg == CA_ARG_GRAVITY_TOGGLE ? !game.gravity :
10584 case CA_SET_LEVEL_WIND:
10586 game.wind_direction = action_arg_direction;
10591 case CA_SET_LEVEL_RANDOM_SEED:
10594 /* ensure that setting a new random seed while playing is predictable */
10595 InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10597 InitRND(action_arg_number_new);
10601 printf("::: %d -> %d\n", action_arg_number_new, RND(10));
10609 for (i = 0; i < 9; i++)
10610 printf("%d, ", RND(2));
10618 /* ---------- player actions ------------------------------------------ */
10620 case CA_MOVE_PLAYER:
10622 /* automatically move to the next field in specified direction */
10623 for (i = 0; i < MAX_PLAYERS; i++)
10624 if (trigger_player_bits & (1 << i))
10625 stored_player[i].programmed_action = action_arg_direction;
10630 case CA_EXIT_PLAYER:
10632 for (i = 0; i < MAX_PLAYERS; i++)
10633 if (action_arg_player_bits & (1 << i))
10634 PlayerWins(&stored_player[i]);
10639 case CA_KILL_PLAYER:
10641 for (i = 0; i < MAX_PLAYERS; i++)
10642 if (action_arg_player_bits & (1 << i))
10643 KillPlayer(&stored_player[i]);
10648 case CA_SET_PLAYER_KEYS:
10650 int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10651 int element = getSpecialActionElement(action_arg_element,
10652 action_arg_number, EL_KEY_1);
10654 if (IS_KEY(element))
10656 for (i = 0; i < MAX_PLAYERS; i++)
10658 if (trigger_player_bits & (1 << i))
10660 stored_player[i].key[KEY_NR(element)] = key_state;
10662 DrawGameDoorValues();
10670 case CA_SET_PLAYER_SPEED:
10673 printf("::: trigger_player_bits == %d\n", trigger_player_bits);
10676 for (i = 0; i < MAX_PLAYERS; i++)
10678 if (trigger_player_bits & (1 << i))
10680 int move_stepsize = TILEX / stored_player[i].move_delay_value;
10682 if (action_arg == CA_ARG_SPEED_FASTER &&
10683 stored_player[i].cannot_move)
10685 action_arg_number = STEPSIZE_VERY_SLOW;
10687 else if (action_arg == CA_ARG_SPEED_SLOWER ||
10688 action_arg == CA_ARG_SPEED_FASTER)
10690 action_arg_number = 2;
10691 action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10694 else if (action_arg == CA_ARG_NUMBER_RESET)
10696 action_arg_number = level.initial_player_stepsize[i];
10700 getModifiedActionNumber(move_stepsize,
10703 action_arg_number_min,
10704 action_arg_number_max);
10706 SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10713 case CA_SET_PLAYER_SHIELD:
10715 for (i = 0; i < MAX_PLAYERS; i++)
10717 if (trigger_player_bits & (1 << i))
10719 if (action_arg == CA_ARG_SHIELD_OFF)
10721 stored_player[i].shield_normal_time_left = 0;
10722 stored_player[i].shield_deadly_time_left = 0;
10724 else if (action_arg == CA_ARG_SHIELD_NORMAL)
10726 stored_player[i].shield_normal_time_left = 999999;
10728 else if (action_arg == CA_ARG_SHIELD_DEADLY)
10730 stored_player[i].shield_normal_time_left = 999999;
10731 stored_player[i].shield_deadly_time_left = 999999;
10739 #if USE_PLAYER_GRAVITY
10740 case CA_SET_PLAYER_GRAVITY:
10742 for (i = 0; i < MAX_PLAYERS; i++)
10744 if (trigger_player_bits & (1 << i))
10746 stored_player[i].gravity =
10747 (action_arg == CA_ARG_GRAVITY_OFF ? FALSE :
10748 action_arg == CA_ARG_GRAVITY_ON ? TRUE :
10749 action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10750 stored_player[i].gravity);
10758 case CA_SET_PLAYER_ARTWORK:
10760 for (i = 0; i < MAX_PLAYERS; i++)
10762 if (trigger_player_bits & (1 << i))
10764 int artwork_element = action_arg_element;
10766 if (action_arg == CA_ARG_ELEMENT_RESET)
10768 (level.use_artwork_element[i] ? level.artwork_element[i] :
10769 stored_player[i].element_nr);
10771 #if USE_GFX_RESET_PLAYER_ARTWORK
10772 if (stored_player[i].artwork_element != artwork_element)
10773 stored_player[i].Frame = 0;
10776 stored_player[i].artwork_element = artwork_element;
10778 SetPlayerWaiting(&stored_player[i], FALSE);
10780 /* set number of special actions for bored and sleeping animation */
10781 stored_player[i].num_special_action_bored =
10782 get_num_special_action(artwork_element,
10783 ACTION_BORING_1, ACTION_BORING_LAST);
10784 stored_player[i].num_special_action_sleeping =
10785 get_num_special_action(artwork_element,
10786 ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10793 case CA_SET_PLAYER_INVENTORY:
10795 for (i = 0; i < MAX_PLAYERS; i++)
10797 struct PlayerInfo *player = &stored_player[i];
10800 if (trigger_player_bits & (1 << i))
10802 int inventory_element = action_arg_element;
10804 if (action_arg == CA_ARG_ELEMENT_TARGET ||
10805 action_arg == CA_ARG_ELEMENT_TRIGGER ||
10806 action_arg == CA_ARG_ELEMENT_ACTION)
10808 int element = inventory_element;
10809 int collect_count = element_info[element].collect_count_initial;
10811 if (!IS_CUSTOM_ELEMENT(element))
10814 if (collect_count == 0)
10815 player->inventory_infinite_element = element;
10817 for (k = 0; k < collect_count; k++)
10818 if (player->inventory_size < MAX_INVENTORY_SIZE)
10819 player->inventory_element[player->inventory_size++] =
10822 else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10823 action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10824 action_arg == CA_ARG_INVENTORY_RM_ACTION)
10826 if (player->inventory_infinite_element != EL_UNDEFINED &&
10827 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10828 action_arg_element_raw))
10829 player->inventory_infinite_element = EL_UNDEFINED;
10831 for (k = 0, j = 0; j < player->inventory_size; j++)
10833 if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10834 action_arg_element_raw))
10835 player->inventory_element[k++] = player->inventory_element[j];
10838 player->inventory_size = k;
10840 else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10842 if (player->inventory_size > 0)
10844 for (j = 0; j < player->inventory_size - 1; j++)
10845 player->inventory_element[j] = player->inventory_element[j + 1];
10847 player->inventory_size--;
10850 else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10852 if (player->inventory_size > 0)
10853 player->inventory_size--;
10855 else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10857 player->inventory_infinite_element = EL_UNDEFINED;
10858 player->inventory_size = 0;
10860 else if (action_arg == CA_ARG_INVENTORY_RESET)
10862 player->inventory_infinite_element = EL_UNDEFINED;
10863 player->inventory_size = 0;
10865 if (level.use_initial_inventory[i])
10867 for (j = 0; j < level.initial_inventory_size[i]; j++)
10869 int element = level.initial_inventory_content[i][j];
10870 int collect_count = element_info[element].collect_count_initial;
10872 if (!IS_CUSTOM_ELEMENT(element))
10875 if (collect_count == 0)
10876 player->inventory_infinite_element = element;
10878 for (k = 0; k < collect_count; k++)
10879 if (player->inventory_size < MAX_INVENTORY_SIZE)
10880 player->inventory_element[player->inventory_size++] =
10891 /* ---------- CE actions ---------------------------------------------- */
10893 case CA_SET_CE_VALUE:
10895 #if USE_NEW_CUSTOM_VALUE
10896 int last_ce_value = CustomValue[x][y];
10898 CustomValue[x][y] = action_arg_number_new;
10900 if (CustomValue[x][y] != last_ce_value)
10902 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10903 CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10905 if (CustomValue[x][y] == 0)
10907 CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10908 CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10916 case CA_SET_CE_SCORE:
10918 #if USE_NEW_CUSTOM_VALUE
10919 int last_ce_score = ei->collect_score;
10921 ei->collect_score = action_arg_number_new;
10923 if (ei->collect_score != last_ce_score)
10925 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10926 CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10928 if (ei->collect_score == 0)
10932 CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10933 CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10936 This is a very special case that seems to be a mixture between
10937 CheckElementChange() and CheckTriggeredElementChange(): while
10938 the first one only affects single elements that are triggered
10939 directly, the second one affects multiple elements in the playfield
10940 that are triggered indirectly by another element. This is a third
10941 case: Changing the CE score always affects multiple identical CEs,
10942 so every affected CE must be checked, not only the single CE for
10943 which the CE score was changed in the first place (as every instance
10944 of that CE shares the same CE score, and therefore also can change)!
10946 SCAN_PLAYFIELD(xx, yy)
10948 if (Feld[xx][yy] == element)
10949 CheckElementChange(xx, yy, element, EL_UNDEFINED,
10950 CE_SCORE_GETS_ZERO);
10959 case CA_SET_CE_ARTWORK:
10961 int artwork_element = action_arg_element;
10962 boolean reset_frame = FALSE;
10965 if (action_arg == CA_ARG_ELEMENT_RESET)
10966 artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10969 if (ei->gfx_element != artwork_element)
10970 reset_frame = TRUE;
10972 ei->gfx_element = artwork_element;
10974 SCAN_PLAYFIELD(xx, yy)
10976 if (Feld[xx][yy] == element)
10980 ResetGfxAnimation(xx, yy);
10981 ResetRandomAnimationValue(xx, yy);
10984 TEST_DrawLevelField(xx, yy);
10991 /* ---------- engine actions ------------------------------------------ */
10993 case CA_SET_ENGINE_SCAN_MODE:
10995 InitPlayfieldScanMode(action_arg);
11005 static void CreateFieldExt(int x, int y, int element, boolean is_change)
11007 int old_element = Feld[x][y];
11008 int new_element = GetElementFromGroupElement(element);
11009 int previous_move_direction = MovDir[x][y];
11010 #if USE_NEW_CUSTOM_VALUE
11011 int last_ce_value = CustomValue[x][y];
11013 boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
11014 boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
11015 boolean add_player_onto_element = (new_element_is_player &&
11016 #if USE_CODE_THAT_BREAKS_SNAKE_BITE
11017 /* this breaks SnakeBite when a snake is
11018 halfway through a door that closes */
11019 /* NOW FIXED AT LEVEL INIT IN files.c */
11020 new_element != EL_SOKOBAN_FIELD_PLAYER &&
11022 IS_WALKABLE(old_element));
11025 /* check if element under the player changes from accessible to unaccessible
11026 (needed for special case of dropping element which then changes) */
11027 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11028 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11036 if (!add_player_onto_element)
11038 if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
11039 RemoveMovingField(x, y);
11043 Feld[x][y] = new_element;
11045 #if !USE_GFX_RESET_GFX_ANIMATION
11046 ResetGfxAnimation(x, y);
11047 ResetRandomAnimationValue(x, y);
11050 if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
11051 MovDir[x][y] = previous_move_direction;
11053 #if USE_NEW_CUSTOM_VALUE
11054 if (element_info[new_element].use_last_ce_value)
11055 CustomValue[x][y] = last_ce_value;
11058 InitField_WithBug1(x, y, FALSE);
11060 new_element = Feld[x][y]; /* element may have changed */
11062 #if USE_GFX_RESET_GFX_ANIMATION
11063 ResetGfxAnimation(x, y);
11064 ResetRandomAnimationValue(x, y);
11067 TEST_DrawLevelField(x, y);
11069 if (GFX_CRUMBLED(new_element))
11070 TEST_DrawLevelFieldCrumbledSandNeighbours(x, y);
11074 /* check if element under the player changes from accessible to unaccessible
11075 (needed for special case of dropping element which then changes) */
11076 /* (must be checked after creating new element for walkable group elements) */
11077 #if USE_FIX_KILLED_BY_NON_WALKABLE
11078 if (IS_PLAYER(x, y) && !player_explosion_protected &&
11079 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11086 if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y) &&
11087 IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
11096 /* "ChangeCount" not set yet to allow "entered by player" change one time */
11097 if (new_element_is_player)
11098 RelocatePlayer(x, y, new_element);
11101 ChangeCount[x][y]++; /* count number of changes in the same frame */
11103 TestIfBadThingTouchesPlayer(x, y);
11104 TestIfPlayerTouchesCustomElement(x, y);
11105 TestIfElementTouchesCustomElement(x, y);
11108 static void CreateField(int x, int y, int element)
11110 CreateFieldExt(x, y, element, FALSE);
11113 static void CreateElementFromChange(int x, int y, int element)
11115 element = GET_VALID_RUNTIME_ELEMENT(element);
11117 #if USE_STOP_CHANGED_ELEMENTS
11118 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11120 int old_element = Feld[x][y];
11122 /* prevent changed element from moving in same engine frame
11123 unless both old and new element can either fall or move */
11124 if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
11125 (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
11130 CreateFieldExt(x, y, element, TRUE);
11133 static boolean ChangeElement(int x, int y, int element, int page)
11135 struct ElementInfo *ei = &element_info[element];
11136 struct ElementChangeInfo *change = &ei->change_page[page];
11137 int ce_value = CustomValue[x][y];
11138 int ce_score = ei->collect_score;
11139 int target_element;
11140 int old_element = Feld[x][y];
11142 /* always use default change event to prevent running into a loop */
11143 if (ChangeEvent[x][y] == -1)
11144 ChangeEvent[x][y] = CE_DELAY;
11146 if (ChangeEvent[x][y] == CE_DELAY)
11148 /* reset actual trigger element, trigger player and action element */
11149 change->actual_trigger_element = EL_EMPTY;
11150 change->actual_trigger_player = EL_EMPTY;
11151 change->actual_trigger_player_bits = CH_PLAYER_NONE;
11152 change->actual_trigger_side = CH_SIDE_NONE;
11153 change->actual_trigger_ce_value = 0;
11154 change->actual_trigger_ce_score = 0;
11157 /* do not change elements more than a specified maximum number of changes */
11158 if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
11161 ChangeCount[x][y]++; /* count number of changes in the same frame */
11163 if (change->explode)
11170 if (change->use_target_content)
11172 boolean complete_replace = TRUE;
11173 boolean can_replace[3][3];
11176 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11179 boolean is_walkable;
11180 boolean is_diggable;
11181 boolean is_collectible;
11182 boolean is_removable;
11183 boolean is_destructible;
11184 int ex = x + xx - 1;
11185 int ey = y + yy - 1;
11186 int content_element = change->target_content.e[xx][yy];
11189 can_replace[xx][yy] = TRUE;
11191 if (ex == x && ey == y) /* do not check changing element itself */
11194 if (content_element == EL_EMPTY_SPACE)
11196 can_replace[xx][yy] = FALSE; /* do not replace border with space */
11201 if (!IN_LEV_FIELD(ex, ey))
11203 can_replace[xx][yy] = FALSE;
11204 complete_replace = FALSE;
11211 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11212 e = MovingOrBlocked2Element(ex, ey);
11214 is_empty = (IS_FREE(ex, ey) ||
11215 (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
11217 is_walkable = (is_empty || IS_WALKABLE(e));
11218 is_diggable = (is_empty || IS_DIGGABLE(e));
11219 is_collectible = (is_empty || IS_COLLECTIBLE(e));
11220 is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
11221 is_removable = (is_diggable || is_collectible);
11223 can_replace[xx][yy] =
11224 (((change->replace_when == CP_WHEN_EMPTY && is_empty) ||
11225 (change->replace_when == CP_WHEN_WALKABLE && is_walkable) ||
11226 (change->replace_when == CP_WHEN_DIGGABLE && is_diggable) ||
11227 (change->replace_when == CP_WHEN_COLLECTIBLE && is_collectible) ||
11228 (change->replace_when == CP_WHEN_REMOVABLE && is_removable) ||
11229 (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
11230 !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
11232 if (!can_replace[xx][yy])
11233 complete_replace = FALSE;
11236 if (!change->only_if_complete || complete_replace)
11238 boolean something_has_changed = FALSE;
11240 if (change->only_if_complete && change->use_random_replace &&
11241 RND(100) < change->random_percentage)
11244 for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
11246 int ex = x + xx - 1;
11247 int ey = y + yy - 1;
11248 int content_element;
11250 if (can_replace[xx][yy] && (!change->use_random_replace ||
11251 RND(100) < change->random_percentage))
11253 if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11254 RemoveMovingField(ex, ey);
11256 ChangeEvent[ex][ey] = ChangeEvent[x][y];
11258 content_element = change->target_content.e[xx][yy];
11259 target_element = GET_TARGET_ELEMENT(element, content_element, change,
11260 ce_value, ce_score);
11262 CreateElementFromChange(ex, ey, target_element);
11264 something_has_changed = TRUE;
11266 /* for symmetry reasons, freeze newly created border elements */
11267 if (ex != x || ey != y)
11268 Stop[ex][ey] = TRUE; /* no more moving in this frame */
11272 if (something_has_changed)
11274 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11275 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11281 target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11282 ce_value, ce_score);
11284 if (element == EL_DIAGONAL_GROWING ||
11285 element == EL_DIAGONAL_SHRINKING)
11287 target_element = Store[x][y];
11289 Store[x][y] = EL_EMPTY;
11292 CreateElementFromChange(x, y, target_element);
11294 PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11295 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11298 /* this uses direct change before indirect change */
11299 CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11304 #if USE_NEW_DELAYED_ACTION
11306 static void HandleElementChange(int x, int y, int page)
11308 int element = MovingOrBlocked2Element(x, y);
11309 struct ElementInfo *ei = &element_info[element];
11310 struct ElementChangeInfo *change = &ei->change_page[page];
11311 boolean handle_action_before_change = FALSE;
11314 if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11315 !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11318 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11319 x, y, element, element_info[element].token_name);
11320 printf("HandleElementChange(): This should never happen!\n");
11325 /* this can happen with classic bombs on walkable, changing elements */
11326 if (!CAN_CHANGE_OR_HAS_ACTION(element))
11329 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11330 ChangeDelay[x][y] = 0;
11336 if (ChangeDelay[x][y] == 0) /* initialize element change */
11338 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11340 if (change->can_change)
11343 /* !!! not clear why graphic animation should be reset at all here !!! */
11344 /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
11345 #if USE_GFX_RESET_WHEN_NOT_MOVING
11346 /* when a custom element is about to change (for example by change delay),
11347 do not reset graphic animation when the custom element is moving */
11348 if (!IS_MOVING(x, y))
11351 ResetGfxAnimation(x, y);
11352 ResetRandomAnimationValue(x, y);
11356 if (change->pre_change_function)
11357 change->pre_change_function(x, y);
11361 ChangeDelay[x][y]--;
11363 if (ChangeDelay[x][y] != 0) /* continue element change */
11365 if (change->can_change)
11367 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11369 if (IS_ANIMATED(graphic))
11370 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11372 if (change->change_function)
11373 change->change_function(x, y);
11376 else /* finish element change */
11378 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11380 page = ChangePage[x][y];
11381 ChangePage[x][y] = -1;
11383 change = &ei->change_page[page];
11386 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11388 ChangeDelay[x][y] = 1; /* try change after next move step */
11389 ChangePage[x][y] = page; /* remember page to use for change */
11395 /* special case: set new level random seed before changing element */
11396 if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11397 handle_action_before_change = TRUE;
11399 if (change->has_action && handle_action_before_change)
11400 ExecuteCustomElementAction(x, y, element, page);
11403 if (change->can_change)
11405 if (ChangeElement(x, y, element, page))
11407 if (change->post_change_function)
11408 change->post_change_function(x, y);
11412 if (change->has_action && !handle_action_before_change)
11413 ExecuteCustomElementAction(x, y, element, page);
11419 static void HandleElementChange(int x, int y, int page)
11421 int element = MovingOrBlocked2Element(x, y);
11422 struct ElementInfo *ei = &element_info[element];
11423 struct ElementChangeInfo *change = &ei->change_page[page];
11426 if (!CAN_CHANGE(element) && !CAN_CHANGE(Back[x][y]))
11429 printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
11430 x, y, element, element_info[element].token_name);
11431 printf("HandleElementChange(): This should never happen!\n");
11436 /* this can happen with classic bombs on walkable, changing elements */
11437 if (!CAN_CHANGE(element))
11440 if (!CAN_CHANGE(Back[x][y])) /* prevent permanent repetition */
11441 ChangeDelay[x][y] = 0;
11447 if (ChangeDelay[x][y] == 0) /* initialize element change */
11449 ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11451 ResetGfxAnimation(x, y);
11452 ResetRandomAnimationValue(x, y);
11454 if (change->pre_change_function)
11455 change->pre_change_function(x, y);
11458 ChangeDelay[x][y]--;
11460 if (ChangeDelay[x][y] != 0) /* continue element change */
11462 int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11464 if (IS_ANIMATED(graphic))
11465 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11467 if (change->change_function)
11468 change->change_function(x, y);
11470 else /* finish element change */
11472 if (ChangePage[x][y] != -1) /* remember page from delayed change */
11474 page = ChangePage[x][y];
11475 ChangePage[x][y] = -1;
11477 change = &ei->change_page[page];
11480 if (IS_MOVING(x, y)) /* never change a running system ;-) */
11482 ChangeDelay[x][y] = 1; /* try change after next move step */
11483 ChangePage[x][y] = page; /* remember page to use for change */
11488 if (ChangeElement(x, y, element, page))
11490 if (change->post_change_function)
11491 change->post_change_function(x, y);
11498 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11499 int trigger_element,
11501 int trigger_player,
11505 boolean change_done_any = FALSE;
11506 int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11509 if (!(trigger_events[trigger_element][trigger_event]))
11513 printf("::: CheckTriggeredElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11514 trigger_event, recursion_loop_depth, recursion_loop_detected,
11515 recursion_loop_element, EL_NAME(recursion_loop_element));
11518 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11520 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11522 int element = EL_CUSTOM_START + i;
11523 boolean change_done = FALSE;
11526 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11527 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11530 for (p = 0; p < element_info[element].num_change_pages; p++)
11532 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11534 if (change->can_change_or_has_action &&
11535 change->has_event[trigger_event] &&
11536 change->trigger_side & trigger_side &&
11537 change->trigger_player & trigger_player &&
11538 change->trigger_page & trigger_page_bits &&
11539 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11541 change->actual_trigger_element = trigger_element;
11542 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11543 change->actual_trigger_player_bits = trigger_player;
11544 change->actual_trigger_side = trigger_side;
11545 change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11546 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11549 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d\n",
11550 element, EL_NAME(element), p);
11553 if ((change->can_change && !change_done) || change->has_action)
11557 SCAN_PLAYFIELD(x, y)
11559 if (Feld[x][y] == element)
11561 if (change->can_change && !change_done)
11563 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11564 /* if element already changed in this frame, not only prevent
11565 another element change (checked in ChangeElement()), but
11566 also prevent additional element actions for this element */
11568 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11569 !level.use_action_after_change_bug)
11574 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- CHANGE\n",
11575 element, EL_NAME(element), p);
11578 ChangeDelay[x][y] = 1;
11579 ChangeEvent[x][y] = trigger_event;
11581 HandleElementChange(x, y, p);
11583 #if USE_NEW_DELAYED_ACTION
11584 else if (change->has_action)
11586 #if USE_FIX_NO_ACTION_AFTER_CHANGE
11587 /* if element already changed in this frame, not only prevent
11588 another element change (checked in ChangeElement()), but
11589 also prevent additional element actions for this element */
11591 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11592 !level.use_action_after_change_bug)
11598 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- ACTION\n",
11599 element, EL_NAME(element), p);
11602 ExecuteCustomElementAction(x, y, element, p);
11603 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11606 if (change->has_action)
11608 ExecuteCustomElementAction(x, y, element, p);
11609 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11615 if (change->can_change)
11617 change_done = TRUE;
11618 change_done_any = TRUE;
11621 printf("::: TRIGGERED CHANGE FOUND: %d ['%s'], %d -- DONE\n",
11622 element, EL_NAME(element), p);
11631 RECURSION_LOOP_DETECTION_END();
11633 return change_done_any;
11636 static boolean CheckElementChangeExt(int x, int y,
11638 int trigger_element,
11640 int trigger_player,
11643 boolean change_done = FALSE;
11646 if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11647 !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11650 if (Feld[x][y] == EL_BLOCKED)
11652 Blocked2Moving(x, y, &x, &y);
11653 element = Feld[x][y];
11657 /* check if element has already changed */
11658 if (Feld[x][y] != element)
11661 /* check if element has already changed or is about to change after moving */
11662 if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11663 Feld[x][y] != element) ||
11665 (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11666 (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11667 ChangePage[x][y] != -1)))
11672 printf("::: CheckElementChangeExt %d ... [%d, %d, %d, '%s']\n",
11673 trigger_event, recursion_loop_depth, recursion_loop_detected,
11674 recursion_loop_element, EL_NAME(recursion_loop_element));
11677 RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11680 printf("::: X: trigger_player_bits == %d\n", trigger_player);
11683 for (p = 0; p < element_info[element].num_change_pages; p++)
11685 struct ElementChangeInfo *change = &element_info[element].change_page[p];
11687 /* check trigger element for all events where the element that is checked
11688 for changing interacts with a directly adjacent element -- this is
11689 different to element changes that affect other elements to change on the
11690 whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11691 boolean check_trigger_element =
11692 (trigger_event == CE_TOUCHING_X ||
11693 trigger_event == CE_HITTING_X ||
11694 trigger_event == CE_HIT_BY_X ||
11696 /* this one was forgotten until 3.2.3 */
11697 trigger_event == CE_DIGGING_X);
11700 if (change->can_change_or_has_action &&
11701 change->has_event[trigger_event] &&
11702 change->trigger_side & trigger_side &&
11703 change->trigger_player & trigger_player &&
11704 (!check_trigger_element ||
11705 IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11707 change->actual_trigger_element = trigger_element;
11708 change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11709 change->actual_trigger_player_bits = trigger_player;
11710 change->actual_trigger_side = trigger_side;
11711 change->actual_trigger_ce_value = CustomValue[x][y];
11712 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11714 /* special case: trigger element not at (x,y) position for some events */
11715 if (check_trigger_element)
11727 { 0, 0 }, { 0, 0 }, { 0, 0 },
11731 int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11732 int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11734 change->actual_trigger_ce_value = CustomValue[xx][yy];
11735 change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11738 if (change->can_change && !change_done)
11740 ChangeDelay[x][y] = 1;
11741 ChangeEvent[x][y] = trigger_event;
11743 HandleElementChange(x, y, p);
11745 change_done = TRUE;
11747 #if USE_NEW_DELAYED_ACTION
11748 else if (change->has_action)
11750 ExecuteCustomElementAction(x, y, element, p);
11751 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11754 if (change->has_action)
11756 ExecuteCustomElementAction(x, y, element, p);
11757 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11763 RECURSION_LOOP_DETECTION_END();
11765 return change_done;
11768 static void PlayPlayerSound(struct PlayerInfo *player)
11770 int jx = player->jx, jy = player->jy;
11771 int sound_element = player->artwork_element;
11772 int last_action = player->last_action_waiting;
11773 int action = player->action_waiting;
11775 if (player->is_waiting)
11777 if (action != last_action)
11778 PlayLevelSoundElementAction(jx, jy, sound_element, action);
11780 PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11784 if (action != last_action)
11785 StopSound(element_info[sound_element].sound[last_action]);
11787 if (last_action == ACTION_SLEEPING)
11788 PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11792 static void PlayAllPlayersSound()
11796 for (i = 0; i < MAX_PLAYERS; i++)
11797 if (stored_player[i].active)
11798 PlayPlayerSound(&stored_player[i]);
11801 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11803 boolean last_waiting = player->is_waiting;
11804 int move_dir = player->MovDir;
11806 player->dir_waiting = move_dir;
11807 player->last_action_waiting = player->action_waiting;
11811 if (!last_waiting) /* not waiting -> waiting */
11813 player->is_waiting = TRUE;
11815 player->frame_counter_bored =
11817 game.player_boring_delay_fixed +
11818 GetSimpleRandom(game.player_boring_delay_random);
11819 player->frame_counter_sleeping =
11821 game.player_sleeping_delay_fixed +
11822 GetSimpleRandom(game.player_sleeping_delay_random);
11824 InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11827 if (game.player_sleeping_delay_fixed +
11828 game.player_sleeping_delay_random > 0 &&
11829 player->anim_delay_counter == 0 &&
11830 player->post_delay_counter == 0 &&
11831 FrameCounter >= player->frame_counter_sleeping)
11832 player->is_sleeping = TRUE;
11833 else if (game.player_boring_delay_fixed +
11834 game.player_boring_delay_random > 0 &&
11835 FrameCounter >= player->frame_counter_bored)
11836 player->is_bored = TRUE;
11838 player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11839 player->is_bored ? ACTION_BORING :
11842 if (player->is_sleeping && player->use_murphy)
11844 /* special case for sleeping Murphy when leaning against non-free tile */
11846 if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11847 (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
11848 !IS_MOVING(player->jx - 1, player->jy)))
11849 move_dir = MV_LEFT;
11850 else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11851 (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
11852 !IS_MOVING(player->jx + 1, player->jy)))
11853 move_dir = MV_RIGHT;
11855 player->is_sleeping = FALSE;
11857 player->dir_waiting = move_dir;
11860 if (player->is_sleeping)
11862 if (player->num_special_action_sleeping > 0)
11864 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11866 int last_special_action = player->special_action_sleeping;
11867 int num_special_action = player->num_special_action_sleeping;
11868 int special_action =
11869 (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11870 last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11871 last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11872 last_special_action + 1 : ACTION_SLEEPING);
11873 int special_graphic =
11874 el_act_dir2img(player->artwork_element, special_action, move_dir);
11876 player->anim_delay_counter =
11877 graphic_info[special_graphic].anim_delay_fixed +
11878 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11879 player->post_delay_counter =
11880 graphic_info[special_graphic].post_delay_fixed +
11881 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11883 player->special_action_sleeping = special_action;
11886 if (player->anim_delay_counter > 0)
11888 player->action_waiting = player->special_action_sleeping;
11889 player->anim_delay_counter--;
11891 else if (player->post_delay_counter > 0)
11893 player->post_delay_counter--;
11897 else if (player->is_bored)
11899 if (player->num_special_action_bored > 0)
11901 if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11903 int special_action =
11904 ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11905 int special_graphic =
11906 el_act_dir2img(player->artwork_element, special_action, move_dir);
11908 player->anim_delay_counter =
11909 graphic_info[special_graphic].anim_delay_fixed +
11910 GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11911 player->post_delay_counter =
11912 graphic_info[special_graphic].post_delay_fixed +
11913 GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11915 player->special_action_bored = special_action;
11918 if (player->anim_delay_counter > 0)
11920 player->action_waiting = player->special_action_bored;
11921 player->anim_delay_counter--;
11923 else if (player->post_delay_counter > 0)
11925 player->post_delay_counter--;
11930 else if (last_waiting) /* waiting -> not waiting */
11932 player->is_waiting = FALSE;
11933 player->is_bored = FALSE;
11934 player->is_sleeping = FALSE;
11936 player->frame_counter_bored = -1;
11937 player->frame_counter_sleeping = -1;
11939 player->anim_delay_counter = 0;
11940 player->post_delay_counter = 0;
11942 player->dir_waiting = player->MovDir;
11943 player->action_waiting = ACTION_DEFAULT;
11945 player->special_action_bored = ACTION_DEFAULT;
11946 player->special_action_sleeping = ACTION_DEFAULT;
11950 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11952 boolean moved = FALSE, snapped = FALSE, dropped = FALSE;
11953 int left = player_action & JOY_LEFT;
11954 int right = player_action & JOY_RIGHT;
11955 int up = player_action & JOY_UP;
11956 int down = player_action & JOY_DOWN;
11957 int button1 = player_action & JOY_BUTTON_1;
11958 int button2 = player_action & JOY_BUTTON_2;
11959 int dx = (left ? -1 : right ? 1 : 0);
11960 int dy = (up ? -1 : down ? 1 : 0);
11962 if (!player->active || tape.pausing)
11968 snapped = SnapField(player, dx, dy);
11972 dropped = DropElement(player);
11974 moved = MovePlayer(player, dx, dy);
11977 if (tape.single_step && tape.recording && !tape.pausing)
11979 if (button1 || (dropped && !moved))
11981 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
11982 SnapField(player, 0, 0); /* stop snapping */
11986 SetPlayerWaiting(player, FALSE);
11988 return player_action;
11992 /* no actions for this player (no input at player's configured device) */
11994 DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11995 SnapField(player, 0, 0);
11996 CheckGravityMovementWhenNotMoving(player);
11998 if (player->MovPos == 0)
11999 SetPlayerWaiting(player, TRUE);
12001 if (player->MovPos == 0) /* needed for tape.playing */
12002 player->is_moving = FALSE;
12004 player->is_dropping = FALSE;
12005 player->is_dropping_pressed = FALSE;
12006 player->drop_pressed_delay = 0;
12012 static void CheckLevelTime()
12016 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12018 if (level.native_em_level->lev->home == 0) /* all players at home */
12020 PlayerWins(local_player);
12022 AllPlayersGone = TRUE;
12024 level.native_em_level->lev->home = -1;
12027 if (level.native_em_level->ply[0]->alive == 0 &&
12028 level.native_em_level->ply[1]->alive == 0 &&
12029 level.native_em_level->ply[2]->alive == 0 &&
12030 level.native_em_level->ply[3]->alive == 0) /* all dead */
12031 AllPlayersGone = TRUE;
12034 if (TimeFrames >= FRAMES_PER_SECOND)
12039 for (i = 0; i < MAX_PLAYERS; i++)
12041 struct PlayerInfo *player = &stored_player[i];
12043 if (SHIELD_ON(player))
12045 player->shield_normal_time_left--;
12047 if (player->shield_deadly_time_left > 0)
12048 player->shield_deadly_time_left--;
12052 if (!local_player->LevelSolved && !level.use_step_counter)
12060 if (TimeLeft <= 10 && setup.time_limit)
12061 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12064 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12066 DisplayGameControlValues();
12068 DrawGameValue_Time(TimeLeft);
12071 if (!TimeLeft && setup.time_limit)
12073 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12074 level.native_em_level->lev->killed_out_of_time = TRUE;
12076 for (i = 0; i < MAX_PLAYERS; i++)
12077 KillPlayer(&stored_player[i]);
12081 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12083 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12085 DisplayGameControlValues();
12088 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
12089 DrawGameValue_Time(TimePlayed);
12092 level.native_em_level->lev->time =
12093 (level.time == 0 ? TimePlayed : TimeLeft);
12096 if (tape.recording || tape.playing)
12097 DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
12101 UpdateAndDisplayGameControlValues();
12103 UpdateGameDoorValues();
12104 DrawGameDoorValues();
12108 void AdvanceFrameAndPlayerCounters(int player_nr)
12112 /* advance frame counters (global frame counter and time frame counter) */
12116 /* advance player counters (counters for move delay, move animation etc.) */
12117 for (i = 0; i < MAX_PLAYERS; i++)
12119 boolean advance_player_counters = (player_nr == -1 || player_nr == i);
12120 int move_delay_value = stored_player[i].move_delay_value;
12121 int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
12123 if (!advance_player_counters) /* not all players may be affected */
12126 #if USE_NEW_PLAYER_ANIM
12127 if (move_frames == 0) /* less than one move per game frame */
12129 int stepsize = TILEX / move_delay_value;
12130 int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
12131 int count = (stored_player[i].is_moving ?
12132 ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
12134 if (count % delay == 0)
12139 stored_player[i].Frame += move_frames;
12141 if (stored_player[i].MovPos != 0)
12142 stored_player[i].StepFrame += move_frames;
12144 if (stored_player[i].move_delay > 0)
12145 stored_player[i].move_delay--;
12147 /* due to bugs in previous versions, counter must count up, not down */
12148 if (stored_player[i].push_delay != -1)
12149 stored_player[i].push_delay++;
12151 if (stored_player[i].drop_delay > 0)
12152 stored_player[i].drop_delay--;
12154 if (stored_player[i].is_dropping_pressed)
12155 stored_player[i].drop_pressed_delay++;
12159 void StartGameActions(boolean init_network_game, boolean record_tape,
12162 unsigned long new_random_seed = InitRND(random_seed);
12165 TapeStartRecording(new_random_seed);
12167 #if defined(NETWORK_AVALIABLE)
12168 if (init_network_game)
12170 SendToServer_StartPlaying();
12181 static unsigned long game_frame_delay = 0;
12182 unsigned long game_frame_delay_value;
12183 byte *recorded_player_action;
12184 byte summarized_player_action = 0;
12185 byte tape_action[MAX_PLAYERS];
12188 /* detect endless loops, caused by custom element programming */
12189 if (recursion_loop_detected && recursion_loop_depth == 0)
12191 char *message = getStringCat3("Internal Error ! Element ",
12192 EL_NAME(recursion_loop_element),
12193 " caused endless loop ! Quit the game ?");
12195 Error(ERR_WARN, "element '%s' caused endless loop in game engine",
12196 EL_NAME(recursion_loop_element));
12198 RequestQuitGameExt(FALSE, level_editor_test_game, message);
12200 recursion_loop_detected = FALSE; /* if game should be continued */
12207 if (game.restart_level)
12208 StartGameActions(options.network, setup.autorecord, level.random_seed);
12210 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12212 if (level.native_em_level->lev->home == 0) /* all players at home */
12214 PlayerWins(local_player);
12216 AllPlayersGone = TRUE;
12218 level.native_em_level->lev->home = -1;
12221 if (level.native_em_level->ply[0]->alive == 0 &&
12222 level.native_em_level->ply[1]->alive == 0 &&
12223 level.native_em_level->ply[2]->alive == 0 &&
12224 level.native_em_level->ply[3]->alive == 0) /* all dead */
12225 AllPlayersGone = TRUE;
12228 if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
12231 if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
12234 if (game_status != GAME_MODE_PLAYING) /* status might have changed */
12237 game_frame_delay_value =
12238 (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12240 if (tape.playing && tape.warp_forward && !tape.pausing)
12241 game_frame_delay_value = 0;
12243 /* ---------- main game synchronization point ---------- */
12245 WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12247 if (network_playing && !network_player_action_received)
12249 /* try to get network player actions in time */
12251 #if defined(NETWORK_AVALIABLE)
12252 /* last chance to get network player actions without main loop delay */
12253 HandleNetworking();
12256 /* game was quit by network peer */
12257 if (game_status != GAME_MODE_PLAYING)
12260 if (!network_player_action_received)
12261 return; /* failed to get network player actions in time */
12263 /* do not yet reset "network_player_action_received" (for tape.pausing) */
12269 /* at this point we know that we really continue executing the game */
12271 network_player_action_received = FALSE;
12273 /* when playing tape, read previously recorded player input from tape data */
12274 recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12277 /* TapePlayAction() may return NULL when toggling to "pause before death" */
12282 if (tape.set_centered_player)
12284 game.centered_player_nr_next = tape.centered_player_nr_next;
12285 game.set_centered_player = TRUE;
12288 for (i = 0; i < MAX_PLAYERS; i++)
12290 summarized_player_action |= stored_player[i].action;
12292 if (!network_playing)
12293 stored_player[i].effective_action = stored_player[i].action;
12296 #if defined(NETWORK_AVALIABLE)
12297 if (network_playing)
12298 SendToServer_MovePlayer(summarized_player_action);
12301 if (!options.network && !setup.team_mode)
12302 local_player->effective_action = summarized_player_action;
12304 if (setup.team_mode && setup.input_on_focus && game.centered_player_nr != -1)
12306 for (i = 0; i < MAX_PLAYERS; i++)
12307 stored_player[i].effective_action =
12308 (i == game.centered_player_nr ? summarized_player_action : 0);
12311 if (recorded_player_action != NULL)
12312 for (i = 0; i < MAX_PLAYERS; i++)
12313 stored_player[i].effective_action = recorded_player_action[i];
12315 for (i = 0; i < MAX_PLAYERS; i++)
12317 tape_action[i] = stored_player[i].effective_action;
12319 /* (this can only happen in the R'n'D game engine) */
12320 if (tape.recording && tape_action[i] && !tape.player_participates[i])
12321 tape.player_participates[i] = TRUE; /* player just appeared from CE */
12324 /* only record actions from input devices, but not programmed actions */
12325 if (tape.recording)
12326 TapeRecordAction(tape_action);
12328 #if USE_NEW_PLAYER_ASSIGNMENTS
12330 byte mapped_action[MAX_PLAYERS];
12332 for (i = 0; i < MAX_PLAYERS; i++)
12333 mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12335 for (i = 0; i < MAX_PLAYERS; i++)
12336 stored_player[i].effective_action = mapped_action[i];
12340 if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12342 GameActions_EM_Main();
12350 void GameActions_EM_Main()
12352 byte effective_action[MAX_PLAYERS];
12353 boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12356 for (i = 0; i < MAX_PLAYERS; i++)
12357 effective_action[i] = stored_player[i].effective_action;
12359 GameActions_EM(effective_action, warp_mode);
12363 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
12366 void GameActions_RND()
12368 int magic_wall_x = 0, magic_wall_y = 0;
12369 int i, x, y, element, graphic;
12371 InitPlayfieldScanModeVars();
12373 #if USE_ONE_MORE_CHANGE_PER_FRAME
12374 if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12376 SCAN_PLAYFIELD(x, y)
12378 ChangeCount[x][y] = 0;
12379 ChangeEvent[x][y] = -1;
12384 if (game.set_centered_player)
12386 boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12388 /* switching to "all players" only possible if all players fit to screen */
12389 if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12391 game.centered_player_nr_next = game.centered_player_nr;
12392 game.set_centered_player = FALSE;
12395 /* do not switch focus to non-existing (or non-active) player */
12396 if (game.centered_player_nr_next >= 0 &&
12397 !stored_player[game.centered_player_nr_next].active)
12399 game.centered_player_nr_next = game.centered_player_nr;
12400 game.set_centered_player = FALSE;
12404 if (game.set_centered_player &&
12405 ScreenMovPos == 0) /* screen currently aligned at tile position */
12409 if (game.centered_player_nr_next == -1)
12411 setScreenCenteredToAllPlayers(&sx, &sy);
12415 sx = stored_player[game.centered_player_nr_next].jx;
12416 sy = stored_player[game.centered_player_nr_next].jy;
12419 game.centered_player_nr = game.centered_player_nr_next;
12420 game.set_centered_player = FALSE;
12422 DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12423 DrawGameDoorValues();
12426 for (i = 0; i < MAX_PLAYERS; i++)
12428 int actual_player_action = stored_player[i].effective_action;
12431 /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12432 - rnd_equinox_tetrachloride 048
12433 - rnd_equinox_tetrachloride_ii 096
12434 - rnd_emanuel_schmieg 002
12435 - doctor_sloan_ww 001, 020
12437 if (stored_player[i].MovPos == 0)
12438 CheckGravityMovement(&stored_player[i]);
12441 /* overwrite programmed action with tape action */
12442 if (stored_player[i].programmed_action)
12443 actual_player_action = stored_player[i].programmed_action;
12445 PlayerActions(&stored_player[i], actual_player_action);
12447 ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12450 ScrollScreen(NULL, SCROLL_GO_ON);
12452 /* for backwards compatibility, the following code emulates a fixed bug that
12453 occured when pushing elements (causing elements that just made their last
12454 pushing step to already (if possible) make their first falling step in the
12455 same game frame, which is bad); this code is also needed to use the famous
12456 "spring push bug" which is used in older levels and might be wanted to be
12457 used also in newer levels, but in this case the buggy pushing code is only
12458 affecting the "spring" element and no other elements */
12460 if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12462 for (i = 0; i < MAX_PLAYERS; i++)
12464 struct PlayerInfo *player = &stored_player[i];
12465 int x = player->jx;
12466 int y = player->jy;
12468 if (player->active && player->is_pushing && player->is_moving &&
12470 (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12471 Feld[x][y] == EL_SPRING))
12473 ContinueMoving(x, y);
12475 /* continue moving after pushing (this is actually a bug) */
12476 if (!IS_MOVING(x, y))
12477 Stop[x][y] = FALSE;
12483 debug_print_timestamp(0, "start main loop profiling");
12486 SCAN_PLAYFIELD(x, y)
12488 ChangeCount[x][y] = 0;
12489 ChangeEvent[x][y] = -1;
12491 /* this must be handled before main playfield loop */
12492 if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
12495 if (MovDelay[x][y] <= 0)
12499 #if USE_NEW_SNAP_DELAY
12500 if (Feld[x][y] == EL_ELEMENT_SNAPPING)
12503 if (MovDelay[x][y] <= 0)
12506 TEST_DrawLevelField(x, y);
12508 TestIfElementTouchesCustomElement(x, y); /* for empty space */
12514 if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12516 printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
12517 printf("GameActions(): This should never happen!\n");
12519 ChangePage[x][y] = -1;
12523 Stop[x][y] = FALSE;
12524 if (WasJustMoving[x][y] > 0)
12525 WasJustMoving[x][y]--;
12526 if (WasJustFalling[x][y] > 0)
12527 WasJustFalling[x][y]--;
12528 if (CheckCollision[x][y] > 0)
12529 CheckCollision[x][y]--;
12530 if (CheckImpact[x][y] > 0)
12531 CheckImpact[x][y]--;
12535 /* reset finished pushing action (not done in ContinueMoving() to allow
12536 continuous pushing animation for elements with zero push delay) */
12537 if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12539 ResetGfxAnimation(x, y);
12540 TEST_DrawLevelField(x, y);
12544 if (IS_BLOCKED(x, y))
12548 Blocked2Moving(x, y, &oldx, &oldy);
12549 if (!IS_MOVING(oldx, oldy))
12551 printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
12552 printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
12553 printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
12554 printf("GameActions(): This should never happen!\n");
12561 debug_print_timestamp(0, "- time for pre-main loop:");
12564 #if 0 // -------------------- !!! TEST ONLY !!! --------------------
12565 SCAN_PLAYFIELD(x, y)
12567 element = Feld[x][y];
12568 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12573 int element2 = element;
12574 int graphic2 = graphic;
12576 int element2 = Feld[x][y];
12577 int graphic2 = el_act_dir2img(element2, GfxAction[x][y], GfxDir[x][y]);
12579 int last_gfx_frame = GfxFrame[x][y];
12581 if (graphic_info[graphic2].anim_global_sync)
12582 GfxFrame[x][y] = FrameCounter;
12583 else if (ANIM_MODE(graphic2) == ANIM_CE_VALUE)
12584 GfxFrame[x][y] = CustomValue[x][y];
12585 else if (ANIM_MODE(graphic2) == ANIM_CE_SCORE)
12586 GfxFrame[x][y] = element_info[element2].collect_score;
12587 else if (ANIM_MODE(graphic2) == ANIM_CE_DELAY)
12588 GfxFrame[x][y] = ChangeDelay[x][y];
12590 if (redraw && GfxFrame[x][y] != last_gfx_frame)
12591 DrawLevelGraphicAnimation(x, y, graphic2);
12594 ResetGfxFrame(x, y, TRUE);
12598 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12599 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12600 ResetRandomAnimationValue(x, y);
12604 SetRandomAnimationValue(x, y);
12608 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12611 #endif // -------------------- !!! TEST ONLY !!! --------------------
12614 debug_print_timestamp(0, "- time for TEST loop: -->");
12617 SCAN_PLAYFIELD(x, y)
12619 element = Feld[x][y];
12620 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12622 ResetGfxFrame(x, y, TRUE);
12624 if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12625 IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12626 ResetRandomAnimationValue(x, y);
12628 SetRandomAnimationValue(x, y);
12630 PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12632 if (IS_INACTIVE(element))
12634 if (IS_ANIMATED(graphic))
12635 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12640 /* this may take place after moving, so 'element' may have changed */
12641 if (IS_CHANGING(x, y) &&
12642 (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12644 int page = element_info[element].event_page_nr[CE_DELAY];
12647 HandleElementChange(x, y, page);
12649 if (CAN_CHANGE(element))
12650 HandleElementChange(x, y, page);
12652 if (HAS_ACTION(element))
12653 ExecuteCustomElementAction(x, y, element, page);
12656 element = Feld[x][y];
12657 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12660 #if 0 // ---------------------------------------------------------------------
12662 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12666 element = Feld[x][y];
12667 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12669 if (IS_ANIMATED(graphic) &&
12670 !IS_MOVING(x, y) &&
12672 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12674 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12675 TEST_DrawTwinkleOnField(x, y);
12677 else if (IS_MOVING(x, y))
12678 ContinueMoving(x, y);
12685 case EL_EM_EXIT_OPEN:
12686 case EL_SP_EXIT_OPEN:
12687 case EL_STEEL_EXIT_OPEN:
12688 case EL_EM_STEEL_EXIT_OPEN:
12689 case EL_SP_TERMINAL:
12690 case EL_SP_TERMINAL_ACTIVE:
12691 case EL_EXTRA_TIME:
12692 case EL_SHIELD_NORMAL:
12693 case EL_SHIELD_DEADLY:
12694 if (IS_ANIMATED(graphic))
12695 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12698 case EL_DYNAMITE_ACTIVE:
12699 case EL_EM_DYNAMITE_ACTIVE:
12700 case EL_DYNABOMB_PLAYER_1_ACTIVE:
12701 case EL_DYNABOMB_PLAYER_2_ACTIVE:
12702 case EL_DYNABOMB_PLAYER_3_ACTIVE:
12703 case EL_DYNABOMB_PLAYER_4_ACTIVE:
12704 case EL_SP_DISK_RED_ACTIVE:
12705 CheckDynamite(x, y);
12708 case EL_AMOEBA_GROWING:
12709 AmoebeWaechst(x, y);
12712 case EL_AMOEBA_SHRINKING:
12713 AmoebaDisappearing(x, y);
12716 #if !USE_NEW_AMOEBA_CODE
12717 case EL_AMOEBA_WET:
12718 case EL_AMOEBA_DRY:
12719 case EL_AMOEBA_FULL:
12721 case EL_EMC_DRIPPER:
12722 AmoebeAbleger(x, y);
12726 case EL_GAME_OF_LIFE:
12731 case EL_EXIT_CLOSED:
12735 case EL_EM_EXIT_CLOSED:
12739 case EL_STEEL_EXIT_CLOSED:
12740 CheckExitSteel(x, y);
12743 case EL_EM_STEEL_EXIT_CLOSED:
12744 CheckExitSteelEM(x, y);
12747 case EL_SP_EXIT_CLOSED:
12751 case EL_EXPANDABLE_WALL_GROWING:
12752 case EL_EXPANDABLE_STEELWALL_GROWING:
12753 MauerWaechst(x, y);
12756 case EL_EXPANDABLE_WALL:
12757 case EL_EXPANDABLE_WALL_HORIZONTAL:
12758 case EL_EXPANDABLE_WALL_VERTICAL:
12759 case EL_EXPANDABLE_WALL_ANY:
12760 case EL_BD_EXPANDABLE_WALL:
12761 MauerAbleger(x, y);
12764 case EL_EXPANDABLE_STEELWALL_HORIZONTAL:
12765 case EL_EXPANDABLE_STEELWALL_VERTICAL:
12766 case EL_EXPANDABLE_STEELWALL_ANY:
12767 MauerAblegerStahl(x, y);
12771 CheckForDragon(x, y);
12777 case EL_ELEMENT_SNAPPING:
12778 case EL_DIAGONAL_SHRINKING:
12779 case EL_DIAGONAL_GROWING:
12782 el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12784 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12789 if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12790 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12795 #else // ---------------------------------------------------------------------
12797 if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12801 element = Feld[x][y];
12802 graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12804 if (IS_ANIMATED(graphic) &&
12805 !IS_MOVING(x, y) &&
12807 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12809 if (IS_GEM(element) || element == EL_SP_INFOTRON)
12810 TEST_DrawTwinkleOnField(x, y);
12812 else if ((element == EL_ACID ||
12813 element == EL_EXIT_OPEN ||
12814 element == EL_EM_EXIT_OPEN ||
12815 element == EL_SP_EXIT_OPEN ||
12816 element == EL_STEEL_EXIT_OPEN ||
12817 element == EL_EM_STEEL_EXIT_OPEN ||
12818 element == EL_SP_TERMINAL ||
12819 element == EL_SP_TERMINAL_ACTIVE ||
12820 element == EL_EXTRA_TIME ||
12821 element == EL_SHIELD_NORMAL ||
12822 element == EL_SHIELD_DEADLY) &&
12823 IS_ANIMATED(graphic))
12824 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12825 else if (IS_MOVING(x, y))
12826 ContinueMoving(x, y);
12827 else if (IS_ACTIVE_BOMB(element))
12828 CheckDynamite(x, y);
12829 else if (element == EL_AMOEBA_GROWING)
12830 AmoebeWaechst(x, y);
12831 else if (element == EL_AMOEBA_SHRINKING)
12832 AmoebaDisappearing(x, y);
12834 #if !USE_NEW_AMOEBA_CODE
12835 else if (IS_AMOEBALIVE(element))
12836 AmoebeAbleger(x, y);
12839 else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12841 else if (element == EL_EXIT_CLOSED)
12843 else if (element == EL_EM_EXIT_CLOSED)
12845 else if (element == EL_STEEL_EXIT_CLOSED)
12846 CheckExitSteel(x, y);
12847 else if (element == EL_EM_STEEL_EXIT_CLOSED)
12848 CheckExitSteelEM(x, y);
12849 else if (element == EL_SP_EXIT_CLOSED)
12851 else if (element == EL_EXPANDABLE_WALL_GROWING ||
12852 element == EL_EXPANDABLE_STEELWALL_GROWING)
12853 MauerWaechst(x, y);
12854 else if (element == EL_EXPANDABLE_WALL ||
12855 element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12856 element == EL_EXPANDABLE_WALL_VERTICAL ||
12857 element == EL_EXPANDABLE_WALL_ANY ||
12858 element == EL_BD_EXPANDABLE_WALL)
12859 MauerAbleger(x, y);
12860 else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12861 element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12862 element == EL_EXPANDABLE_STEELWALL_ANY)
12863 MauerAblegerStahl(x, y);
12864 else if (element == EL_FLAMES)
12865 CheckForDragon(x, y);
12866 else if (element == EL_EXPLOSION)
12867 ; /* drawing of correct explosion animation is handled separately */
12868 else if (element == EL_ELEMENT_SNAPPING ||
12869 element == EL_DIAGONAL_SHRINKING ||
12870 element == EL_DIAGONAL_GROWING)
12872 graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12874 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12876 else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12877 DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12879 #endif // ---------------------------------------------------------------------
12881 if (IS_BELT_ACTIVE(element))
12882 PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12884 if (game.magic_wall_active)
12886 int jx = local_player->jx, jy = local_player->jy;
12888 /* play the element sound at the position nearest to the player */
12889 if ((element == EL_MAGIC_WALL_FULL ||
12890 element == EL_MAGIC_WALL_ACTIVE ||
12891 element == EL_MAGIC_WALL_EMPTYING ||
12892 element == EL_BD_MAGIC_WALL_FULL ||
12893 element == EL_BD_MAGIC_WALL_ACTIVE ||
12894 element == EL_BD_MAGIC_WALL_EMPTYING ||
12895 element == EL_DC_MAGIC_WALL_FULL ||
12896 element == EL_DC_MAGIC_WALL_ACTIVE ||
12897 element == EL_DC_MAGIC_WALL_EMPTYING) &&
12898 ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
12907 debug_print_timestamp(0, "- time for MAIN loop: -->");
12910 #if USE_NEW_AMOEBA_CODE
12911 /* new experimental amoeba growth stuff */
12912 if (!(FrameCounter % 8))
12914 static unsigned long random = 1684108901;
12916 for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12918 x = RND(lev_fieldx);
12919 y = RND(lev_fieldy);
12920 element = Feld[x][y];
12922 if (!IS_PLAYER(x,y) &&
12923 (element == EL_EMPTY ||
12924 CAN_GROW_INTO(element) ||
12925 element == EL_QUICKSAND_EMPTY ||
12926 element == EL_QUICKSAND_FAST_EMPTY ||
12927 element == EL_ACID_SPLASH_LEFT ||
12928 element == EL_ACID_SPLASH_RIGHT))
12930 if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
12931 (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
12932 (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
12933 (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
12934 Feld[x][y] = EL_AMOEBA_DROP;
12937 random = random * 129 + 1;
12943 if (game.explosions_delayed)
12946 game.explosions_delayed = FALSE;
12948 SCAN_PLAYFIELD(x, y)
12950 element = Feld[x][y];
12952 if (ExplodeField[x][y])
12953 Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12954 else if (element == EL_EXPLOSION)
12955 Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12957 ExplodeField[x][y] = EX_TYPE_NONE;
12960 game.explosions_delayed = TRUE;
12963 if (game.magic_wall_active)
12965 if (!(game.magic_wall_time_left % 4))
12967 int element = Feld[magic_wall_x][magic_wall_y];
12969 if (element == EL_BD_MAGIC_WALL_FULL ||
12970 element == EL_BD_MAGIC_WALL_ACTIVE ||
12971 element == EL_BD_MAGIC_WALL_EMPTYING)
12972 PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12973 else if (element == EL_DC_MAGIC_WALL_FULL ||
12974 element == EL_DC_MAGIC_WALL_ACTIVE ||
12975 element == EL_DC_MAGIC_WALL_EMPTYING)
12976 PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12978 PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12981 if (game.magic_wall_time_left > 0)
12983 game.magic_wall_time_left--;
12985 if (!game.magic_wall_time_left)
12987 SCAN_PLAYFIELD(x, y)
12989 element = Feld[x][y];
12991 if (element == EL_MAGIC_WALL_ACTIVE ||
12992 element == EL_MAGIC_WALL_FULL)
12994 Feld[x][y] = EL_MAGIC_WALL_DEAD;
12995 TEST_DrawLevelField(x, y);
12997 else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12998 element == EL_BD_MAGIC_WALL_FULL)
13000 Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
13001 TEST_DrawLevelField(x, y);
13003 else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
13004 element == EL_DC_MAGIC_WALL_FULL)
13006 Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
13007 TEST_DrawLevelField(x, y);
13011 game.magic_wall_active = FALSE;
13016 if (game.light_time_left > 0)
13018 game.light_time_left--;
13020 if (game.light_time_left == 0)
13021 RedrawAllLightSwitchesAndInvisibleElements();
13024 if (game.timegate_time_left > 0)
13026 game.timegate_time_left--;
13028 if (game.timegate_time_left == 0)
13029 CloseAllOpenTimegates();
13032 if (game.lenses_time_left > 0)
13034 game.lenses_time_left--;
13036 if (game.lenses_time_left == 0)
13037 RedrawAllInvisibleElementsForLenses();
13040 if (game.magnify_time_left > 0)
13042 game.magnify_time_left--;
13044 if (game.magnify_time_left == 0)
13045 RedrawAllInvisibleElementsForMagnifier();
13048 for (i = 0; i < MAX_PLAYERS; i++)
13050 struct PlayerInfo *player = &stored_player[i];
13052 if (SHIELD_ON(player))
13054 if (player->shield_deadly_time_left)
13055 PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
13056 else if (player->shield_normal_time_left)
13057 PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
13061 #if USE_DELAYED_GFX_REDRAW
13062 SCAN_PLAYFIELD(x, y)
13065 if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
13067 if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)) &&
13068 GfxRedraw[x][y] != GFX_REDRAW_NONE)
13071 /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
13072 !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
13074 if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
13075 DrawLevelField(x, y);
13077 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
13078 DrawLevelFieldCrumbledSand(x, y);
13080 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
13081 DrawLevelFieldCrumbledSandNeighbours(x, y);
13083 if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
13084 DrawTwinkleOnField(x, y);
13087 GfxRedraw[x][y] = GFX_REDRAW_NONE;
13094 PlayAllPlayersSound();
13096 if (options.debug) /* calculate frames per second */
13098 static unsigned long fps_counter = 0;
13099 static int fps_frames = 0;
13100 unsigned long fps_delay_ms = Counter() - fps_counter;
13104 if (fps_delay_ms >= 500) /* calculate fps every 0.5 seconds */
13106 global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
13109 fps_counter = Counter();
13112 redraw_mask |= REDRAW_FPS;
13115 AdvanceFrameAndPlayerCounters(-1); /* advance counters for all players */
13117 if (local_player->show_envelope != 0 && local_player->MovPos == 0)
13119 ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
13121 local_player->show_envelope = 0;
13125 debug_print_timestamp(0, "stop main loop profiling ");
13126 printf("----------------------------------------------------------\n");
13129 /* use random number generator in every frame to make it less predictable */
13130 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13134 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
13136 int min_x = x, min_y = y, max_x = x, max_y = y;
13139 for (i = 0; i < MAX_PLAYERS; i++)
13141 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13143 if (!stored_player[i].active || &stored_player[i] == player)
13146 min_x = MIN(min_x, jx);
13147 min_y = MIN(min_y, jy);
13148 max_x = MAX(max_x, jx);
13149 max_y = MAX(max_y, jy);
13152 return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
13155 static boolean AllPlayersInVisibleScreen()
13159 for (i = 0; i < MAX_PLAYERS; i++)
13161 int jx = stored_player[i].jx, jy = stored_player[i].jy;
13163 if (!stored_player[i].active)
13166 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13173 void ScrollLevel(int dx, int dy)
13176 /* (directly solved in BlitBitmap() now) */
13177 static Bitmap *bitmap_db_field2 = NULL;
13178 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13185 /* !!! THIS IS APPARENTLY WRONG FOR PLAYER RELOCATION !!! */
13186 /* only horizontal XOR vertical scroll direction allowed */
13187 if ((dx == 0 && dy == 0) || (dx != 0 && dy != 0))
13192 /* (directly solved in BlitBitmap() now) */
13193 if (bitmap_db_field2 == NULL)
13194 bitmap_db_field2 = CreateBitmap(FXSIZE, FYSIZE, DEFAULT_DEPTH);
13196 /* needed when blitting directly to same bitmap -- should not be needed with
13197 recent SDL libraries, but apparently does not work in 1.2.11 directly */
13198 BlitBitmap(drawto_field, bitmap_db_field2,
13199 FX + TILEX * (dx == -1) - softscroll_offset,
13200 FY + TILEY * (dy == -1) - softscroll_offset,
13201 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13202 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13203 FX + TILEX * (dx == 1) - softscroll_offset,
13204 FY + TILEY * (dy == 1) - softscroll_offset);
13205 BlitBitmap(bitmap_db_field2, drawto_field,
13206 FX + TILEX * (dx == 1) - softscroll_offset,
13207 FY + TILEY * (dy == 1) - softscroll_offset,
13208 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13209 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13210 FX + TILEX * (dx == 1) - softscroll_offset,
13211 FY + TILEY * (dy == 1) - softscroll_offset);
13216 /* !!! DOES NOT WORK FOR DIAGONAL PLAYER RELOCATION !!! */
13217 int xsize = (BX2 - BX1 + 1);
13218 int ysize = (BY2 - BY1 + 1);
13219 int start = (dx != 0 ? (dx == -1 ? BX1 : BX2) : (dy == -1 ? BY1 : BY2));
13220 int end = (dx != 0 ? (dx == -1 ? BX2 : BX1) : (dy == -1 ? BY2 : BY1));
13221 int step = (start < end ? +1 : -1);
13223 for (i = start; i != end; i += step)
13225 BlitBitmap(drawto_field, drawto_field,
13226 FX + TILEX * (dx != 0 ? i + step : 0),
13227 FY + TILEY * (dy != 0 ? i + step : 0),
13228 TILEX * (dx != 0 ? 1 : xsize),
13229 TILEY * (dy != 0 ? 1 : ysize),
13230 FX + TILEX * (dx != 0 ? i : 0),
13231 FY + TILEY * (dy != 0 ? i : 0));
13236 int softscroll_offset = (setup.soft_scrolling ? TILEX : 0);
13238 BlitBitmap(drawto_field, drawto_field,
13239 FX + TILEX * (dx == -1) - softscroll_offset,
13240 FY + TILEY * (dy == -1) - softscroll_offset,
13241 SXSIZE - TILEX * (dx != 0) + 2 * softscroll_offset,
13242 SYSIZE - TILEY * (dy != 0) + 2 * softscroll_offset,
13243 FX + TILEX * (dx == 1) - softscroll_offset,
13244 FY + TILEY * (dy == 1) - softscroll_offset);
13250 x = (dx == 1 ? BX1 : BX2);
13251 for (y = BY1; y <= BY2; y++)
13252 DrawScreenField(x, y);
13257 y = (dy == 1 ? BY1 : BY2);
13258 for (x = BX1; x <= BX2; x++)
13259 DrawScreenField(x, y);
13262 redraw_mask |= REDRAW_FIELD;
13265 static boolean canFallDown(struct PlayerInfo *player)
13267 int jx = player->jx, jy = player->jy;
13269 return (IN_LEV_FIELD(jx, jy + 1) &&
13270 (IS_FREE(jx, jy + 1) ||
13271 (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13272 IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
13273 !IS_WALKABLE_INSIDE(Feld[jx][jy]));
13276 static boolean canPassField(int x, int y, int move_dir)
13278 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13279 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13280 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13281 int nextx = x + dx;
13282 int nexty = y + dy;
13283 int element = Feld[x][y];
13285 return (IS_PASSABLE_FROM(element, opposite_dir) &&
13286 !CAN_MOVE(element) &&
13287 IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13288 IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
13289 (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13292 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13294 int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13295 int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13296 int dy = (move_dir & MV_UP ? -1 : move_dir & MV_DOWN ? +1 : 0);
13300 return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13301 IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
13302 (IS_DIGGABLE(Feld[newx][newy]) ||
13303 IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
13304 canPassField(newx, newy, move_dir)));
13307 static void CheckGravityMovement(struct PlayerInfo *player)
13309 #if USE_PLAYER_GRAVITY
13310 if (player->gravity && !player->programmed_action)
13312 if (game.gravity && !player->programmed_action)
13315 int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13316 int move_dir_vertical = player->effective_action & MV_VERTICAL;
13317 boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13318 int jx = player->jx, jy = player->jy;
13319 boolean player_is_moving_to_valid_field =
13320 (!player_is_snapping &&
13321 (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13322 canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13323 boolean player_can_fall_down = canFallDown(player);
13325 if (player_can_fall_down &&
13326 !player_is_moving_to_valid_field)
13327 player->programmed_action = MV_DOWN;
13331 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13333 return CheckGravityMovement(player);
13335 #if USE_PLAYER_GRAVITY
13336 if (player->gravity && !player->programmed_action)
13338 if (game.gravity && !player->programmed_action)
13341 int jx = player->jx, jy = player->jy;
13342 boolean field_under_player_is_free =
13343 (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13344 boolean player_is_standing_on_valid_field =
13345 (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
13346 (IS_WALKABLE(Feld[jx][jy]) &&
13347 !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
13349 if (field_under_player_is_free && !player_is_standing_on_valid_field)
13350 player->programmed_action = MV_DOWN;
13355 MovePlayerOneStep()
13356 -----------------------------------------------------------------------------
13357 dx, dy: direction (non-diagonal) to try to move the player to
13358 real_dx, real_dy: direction as read from input device (can be diagonal)
13361 boolean MovePlayerOneStep(struct PlayerInfo *player,
13362 int dx, int dy, int real_dx, int real_dy)
13364 int jx = player->jx, jy = player->jy;
13365 int new_jx = jx + dx, new_jy = jy + dy;
13366 #if !USE_FIXED_DONT_RUN_INTO
13370 boolean player_can_move = !player->cannot_move;
13372 if (!player->active || (!dx && !dy))
13373 return MP_NO_ACTION;
13375 player->MovDir = (dx < 0 ? MV_LEFT :
13376 dx > 0 ? MV_RIGHT :
13378 dy > 0 ? MV_DOWN : MV_NONE);
13380 if (!IN_LEV_FIELD(new_jx, new_jy))
13381 return MP_NO_ACTION;
13383 if (!player_can_move)
13385 if (player->MovPos == 0)
13387 player->is_moving = FALSE;
13388 player->is_digging = FALSE;
13389 player->is_collecting = FALSE;
13390 player->is_snapping = FALSE;
13391 player->is_pushing = FALSE;
13396 if (!options.network && game.centered_player_nr == -1 &&
13397 !AllPlayersInSight(player, new_jx, new_jy))
13398 return MP_NO_ACTION;
13400 if (!options.network && !AllPlayersInSight(player, new_jx, new_jy))
13401 return MP_NO_ACTION;
13404 #if !USE_FIXED_DONT_RUN_INTO
13405 element = MovingOrBlocked2ElementIfNotLeaving(new_jx, new_jy);
13407 /* (moved to DigField()) */
13408 if (player_can_move && DONT_RUN_INTO(element))
13410 if (element == EL_ACID && dx == 0 && dy == 1)
13412 SplashAcid(new_jx, new_jy);
13413 Feld[jx][jy] = EL_PLAYER_1;
13414 InitMovingField(jx, jy, MV_DOWN);
13415 Store[jx][jy] = EL_ACID;
13416 ContinueMoving(jx, jy);
13417 BuryPlayer(player);
13420 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13426 can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
13427 if (can_move != MP_MOVING)
13430 /* check if DigField() has caused relocation of the player */
13431 if (player->jx != jx || player->jy != jy)
13432 return MP_NO_ACTION; /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
13434 StorePlayer[jx][jy] = 0;
13435 player->last_jx = jx;
13436 player->last_jy = jy;
13437 player->jx = new_jx;
13438 player->jy = new_jy;
13439 StorePlayer[new_jx][new_jy] = player->element_nr;
13441 if (player->move_delay_value_next != -1)
13443 player->move_delay_value = player->move_delay_value_next;
13444 player->move_delay_value_next = -1;
13448 (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13450 player->step_counter++;
13452 PlayerVisit[jx][jy] = FrameCounter;
13454 #if USE_UFAST_PLAYER_EXIT_BUGFIX
13455 player->is_moving = TRUE;
13459 /* should better be called in MovePlayer(), but this breaks some tapes */
13460 ScrollPlayer(player, SCROLL_INIT);
13466 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13468 int jx = player->jx, jy = player->jy;
13469 int old_jx = jx, old_jy = jy;
13470 int moved = MP_NO_ACTION;
13472 if (!player->active)
13477 if (player->MovPos == 0)
13479 player->is_moving = FALSE;
13480 player->is_digging = FALSE;
13481 player->is_collecting = FALSE;
13482 player->is_snapping = FALSE;
13483 player->is_pushing = FALSE;
13489 if (player->move_delay > 0)
13492 player->move_delay = -1; /* set to "uninitialized" value */
13494 /* store if player is automatically moved to next field */
13495 player->is_auto_moving = (player->programmed_action != MV_NONE);
13497 /* remove the last programmed player action */
13498 player->programmed_action = 0;
13500 if (player->MovPos)
13502 /* should only happen if pre-1.2 tape recordings are played */
13503 /* this is only for backward compatibility */
13505 int original_move_delay_value = player->move_delay_value;
13508 printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%ld]\n",
13512 /* scroll remaining steps with finest movement resolution */
13513 player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13515 while (player->MovPos)
13517 ScrollPlayer(player, SCROLL_GO_ON);
13518 ScrollScreen(NULL, SCROLL_GO_ON);
13520 AdvanceFrameAndPlayerCounters(player->index_nr);
13526 player->move_delay_value = original_move_delay_value;
13529 player->is_active = FALSE;
13531 if (player->last_move_dir & MV_HORIZONTAL)
13533 if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13534 moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13538 if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13539 moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13542 #if USE_FIXED_BORDER_RUNNING_GFX
13543 if (!moved && !player->is_active)
13545 player->is_moving = FALSE;
13546 player->is_digging = FALSE;
13547 player->is_collecting = FALSE;
13548 player->is_snapping = FALSE;
13549 player->is_pushing = FALSE;
13557 if (moved & MP_MOVING && !ScreenMovPos &&
13558 (player->index_nr == game.centered_player_nr ||
13559 game.centered_player_nr == -1))
13561 if (moved & MP_MOVING && !ScreenMovPos &&
13562 (player == local_player || !options.network))
13565 int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13566 int offset = game.scroll_delay_value;
13568 if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13570 /* actual player has left the screen -- scroll in that direction */
13571 if (jx != old_jx) /* player has moved horizontally */
13572 scroll_x += (jx - old_jx);
13573 else /* player has moved vertically */
13574 scroll_y += (jy - old_jy);
13578 if (jx != old_jx) /* player has moved horizontally */
13580 if ((player->MovDir == MV_LEFT && scroll_x > jx - MIDPOSX + offset) ||
13581 (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
13582 scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
13584 /* don't scroll over playfield boundaries */
13585 if (scroll_x < SBX_Left || scroll_x > SBX_Right)
13586 scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
13588 /* don't scroll more than one field at a time */
13589 scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13591 /* don't scroll against the player's moving direction */
13592 if ((player->MovDir == MV_LEFT && scroll_x > old_scroll_x) ||
13593 (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13594 scroll_x = old_scroll_x;
13596 else /* player has moved vertically */
13598 if ((player->MovDir == MV_UP && scroll_y > jy - MIDPOSY + offset) ||
13599 (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
13600 scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
13602 /* don't scroll over playfield boundaries */
13603 if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
13604 scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
13606 /* don't scroll more than one field at a time */
13607 scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13609 /* don't scroll against the player's moving direction */
13610 if ((player->MovDir == MV_UP && scroll_y > old_scroll_y) ||
13611 (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13612 scroll_y = old_scroll_y;
13616 if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13619 if (!options.network && game.centered_player_nr == -1 &&
13620 !AllPlayersInVisibleScreen())
13622 scroll_x = old_scroll_x;
13623 scroll_y = old_scroll_y;
13627 if (!options.network && !AllPlayersInVisibleScreen())
13629 scroll_x = old_scroll_x;
13630 scroll_y = old_scroll_y;
13635 ScrollScreen(player, SCROLL_INIT);
13636 ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13641 player->StepFrame = 0;
13643 if (moved & MP_MOVING)
13645 if (old_jx != jx && old_jy == jy)
13646 player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13647 else if (old_jx == jx && old_jy != jy)
13648 player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13650 TEST_DrawLevelField(jx, jy); /* for "crumbled sand" */
13652 player->last_move_dir = player->MovDir;
13653 player->is_moving = TRUE;
13654 player->is_snapping = FALSE;
13655 player->is_switching = FALSE;
13656 player->is_dropping = FALSE;
13657 player->is_dropping_pressed = FALSE;
13658 player->drop_pressed_delay = 0;
13661 /* should better be called here than above, but this breaks some tapes */
13662 ScrollPlayer(player, SCROLL_INIT);
13667 CheckGravityMovementWhenNotMoving(player);
13669 player->is_moving = FALSE;
13671 /* at this point, the player is allowed to move, but cannot move right now
13672 (e.g. because of something blocking the way) -- ensure that the player
13673 is also allowed to move in the next frame (in old versions before 3.1.1,
13674 the player was forced to wait again for eight frames before next try) */
13676 if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13677 player->move_delay = 0; /* allow direct movement in the next frame */
13680 if (player->move_delay == -1) /* not yet initialized by DigField() */
13681 player->move_delay = player->move_delay_value;
13683 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13685 TestIfPlayerTouchesBadThing(jx, jy);
13686 TestIfPlayerTouchesCustomElement(jx, jy);
13689 if (!player->active)
13690 RemovePlayer(player);
13695 void ScrollPlayer(struct PlayerInfo *player, int mode)
13697 int jx = player->jx, jy = player->jy;
13698 int last_jx = player->last_jx, last_jy = player->last_jy;
13699 int move_stepsize = TILEX / player->move_delay_value;
13701 #if USE_NEW_PLAYER_SPEED
13702 if (!player->active)
13705 if (player->MovPos == 0 && mode == SCROLL_GO_ON) /* player not moving */
13708 if (!player->active || player->MovPos == 0)
13712 if (mode == SCROLL_INIT)
13714 player->actual_frame_counter = FrameCounter;
13715 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13717 if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13718 Feld[last_jx][last_jy] == EL_EMPTY)
13720 int last_field_block_delay = 0; /* start with no blocking at all */
13721 int block_delay_adjustment = player->block_delay_adjustment;
13723 /* if player blocks last field, add delay for exactly one move */
13724 if (player->block_last_field)
13726 last_field_block_delay += player->move_delay_value;
13728 /* when blocking enabled, prevent moving up despite gravity */
13729 #if USE_PLAYER_GRAVITY
13730 if (player->gravity && player->MovDir == MV_UP)
13731 block_delay_adjustment = -1;
13733 if (game.gravity && player->MovDir == MV_UP)
13734 block_delay_adjustment = -1;
13738 /* add block delay adjustment (also possible when not blocking) */
13739 last_field_block_delay += block_delay_adjustment;
13741 Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13742 MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13745 #if USE_NEW_PLAYER_SPEED
13746 if (player->MovPos != 0) /* player has not yet reached destination */
13752 else if (!FrameReached(&player->actual_frame_counter, 1))
13755 #if USE_NEW_PLAYER_SPEED
13756 if (player->MovPos != 0)
13758 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13759 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13761 /* before DrawPlayer() to draw correct player graphic for this case */
13762 if (player->MovPos == 0)
13763 CheckGravityMovement(player);
13766 player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13767 player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13769 /* before DrawPlayer() to draw correct player graphic for this case */
13770 if (player->MovPos == 0)
13771 CheckGravityMovement(player);
13774 if (player->MovPos == 0) /* player reached destination field */
13776 if (player->move_delay_reset_counter > 0)
13778 player->move_delay_reset_counter--;
13780 if (player->move_delay_reset_counter == 0)
13782 /* continue with normal speed after quickly moving through gate */
13783 HALVE_PLAYER_SPEED(player);
13785 /* be able to make the next move without delay */
13786 player->move_delay = 0;
13790 player->last_jx = jx;
13791 player->last_jy = jy;
13793 if (Feld[jx][jy] == EL_EXIT_OPEN ||
13794 Feld[jx][jy] == EL_EM_EXIT_OPEN ||
13796 Feld[jx][jy] == EL_EM_EXIT_OPENING ||
13798 Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
13799 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13801 Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13803 Feld[jx][jy] == EL_SP_EXIT_OPEN ||
13804 Feld[jx][jy] == EL_SP_EXIT_OPENING) /* <-- special case */
13806 DrawPlayer(player); /* needed here only to cleanup last field */
13807 RemovePlayer(player);
13809 if (local_player->friends_still_needed == 0 ||
13810 IS_SP_ELEMENT(Feld[jx][jy]))
13811 PlayerWins(player);
13814 /* this breaks one level: "machine", level 000 */
13816 int move_direction = player->MovDir;
13817 int enter_side = MV_DIR_OPPOSITE(move_direction);
13818 int leave_side = move_direction;
13819 int old_jx = last_jx;
13820 int old_jy = last_jy;
13821 int old_element = Feld[old_jx][old_jy];
13822 int new_element = Feld[jx][jy];
13824 if (IS_CUSTOM_ELEMENT(old_element))
13825 CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13827 player->index_bit, leave_side);
13829 CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13830 CE_PLAYER_LEAVES_X,
13831 player->index_bit, leave_side);
13833 if (IS_CUSTOM_ELEMENT(new_element))
13834 CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13835 player->index_bit, enter_side);
13837 CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13838 CE_PLAYER_ENTERS_X,
13839 player->index_bit, enter_side);
13841 #if USE_FIX_CE_ACTION_WITH_PLAYER
13842 CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13843 CE_MOVE_OF_X, move_direction);
13845 CheckTriggeredElementChangeBySide(jx, jy, player->element_nr,
13846 CE_MOVE_OF_X, move_direction);
13850 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13852 TestIfPlayerTouchesBadThing(jx, jy);
13853 TestIfPlayerTouchesCustomElement(jx, jy);
13855 /* needed because pushed element has not yet reached its destination,
13856 so it would trigger a change event at its previous field location */
13857 if (!player->is_pushing)
13858 TestIfElementTouchesCustomElement(jx, jy); /* for empty space */
13860 if (!player->active)
13861 RemovePlayer(player);
13864 if (!local_player->LevelSolved && level.use_step_counter)
13874 if (TimeLeft <= 10 && setup.time_limit)
13875 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13878 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13880 DisplayGameControlValues();
13882 DrawGameValue_Time(TimeLeft);
13885 if (!TimeLeft && setup.time_limit)
13886 for (i = 0; i < MAX_PLAYERS; i++)
13887 KillPlayer(&stored_player[i]);
13890 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13892 game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13894 DisplayGameControlValues();
13897 else if (level.time == 0 && !AllPlayersGone) /* level w/o time limit */
13898 DrawGameValue_Time(TimePlayed);
13902 if (tape.single_step && tape.recording && !tape.pausing &&
13903 !player->programmed_action)
13904 TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13908 void ScrollScreen(struct PlayerInfo *player, int mode)
13910 static unsigned long screen_frame_counter = 0;
13912 if (mode == SCROLL_INIT)
13914 /* set scrolling step size according to actual player's moving speed */
13915 ScrollStepSize = TILEX / player->move_delay_value;
13917 screen_frame_counter = FrameCounter;
13918 ScreenMovDir = player->MovDir;
13919 ScreenMovPos = player->MovPos;
13920 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13923 else if (!FrameReached(&screen_frame_counter, 1))
13928 ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13929 ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13930 redraw_mask |= REDRAW_FIELD;
13933 ScreenMovDir = MV_NONE;
13936 void TestIfPlayerTouchesCustomElement(int x, int y)
13938 static int xy[4][2] =
13945 static int trigger_sides[4][2] =
13947 /* center side border side */
13948 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
13949 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
13950 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
13951 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
13953 static int touch_dir[4] =
13955 MV_LEFT | MV_RIGHT,
13960 int center_element = Feld[x][y]; /* should always be non-moving! */
13963 for (i = 0; i < NUM_DIRECTIONS; i++)
13965 int xx = x + xy[i][0];
13966 int yy = y + xy[i][1];
13967 int center_side = trigger_sides[i][0];
13968 int border_side = trigger_sides[i][1];
13969 int border_element;
13971 if (!IN_LEV_FIELD(xx, yy))
13974 if (IS_PLAYER(x, y)) /* player found at center element */
13976 struct PlayerInfo *player = PLAYERINFO(x, y);
13978 if (game.engine_version < VERSION_IDENT(3,0,7,0))
13979 border_element = Feld[xx][yy]; /* may be moving! */
13980 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13981 border_element = Feld[xx][yy];
13982 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
13983 border_element = MovingOrBlocked2Element(xx, yy);
13985 continue; /* center and border element do not touch */
13987 CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13988 player->index_bit, border_side);
13989 CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13990 CE_PLAYER_TOUCHES_X,
13991 player->index_bit, border_side);
13993 #if USE_FIX_CE_ACTION_WITH_PLAYER
13995 /* use player element that is initially defined in the level playfield,
13996 not the player element that corresponds to the runtime player number
13997 (example: a level that contains EL_PLAYER_3 as the only player would
13998 incorrectly give EL_PLAYER_1 for "player->element_nr") */
13999 int player_element = PLAYERINFO(x, y)->initial_element;
14001 CheckElementChangeBySide(xx, yy, border_element, player_element,
14002 CE_TOUCHING_X, border_side);
14006 else if (IS_PLAYER(xx, yy)) /* player found at border element */
14008 struct PlayerInfo *player = PLAYERINFO(xx, yy);
14010 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14012 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14013 continue; /* center and border element do not touch */
14016 CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
14017 player->index_bit, center_side);
14018 CheckTriggeredElementChangeByPlayer(x, y, center_element,
14019 CE_PLAYER_TOUCHES_X,
14020 player->index_bit, center_side);
14022 #if USE_FIX_CE_ACTION_WITH_PLAYER
14024 /* use player element that is initially defined in the level playfield,
14025 not the player element that corresponds to the runtime player number
14026 (example: a level that contains EL_PLAYER_3 as the only player would
14027 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14028 int player_element = PLAYERINFO(xx, yy)->initial_element;
14030 CheckElementChangeBySide(x, y, center_element, player_element,
14031 CE_TOUCHING_X, center_side);
14040 #if USE_ELEMENT_TOUCHING_BUGFIX
14042 void TestIfElementTouchesCustomElement(int x, int y)
14044 static int xy[4][2] =
14051 static int trigger_sides[4][2] =
14053 /* center side border side */
14054 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14055 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14056 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14057 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14059 static int touch_dir[4] =
14061 MV_LEFT | MV_RIGHT,
14066 boolean change_center_element = FALSE;
14067 int center_element = Feld[x][y]; /* should always be non-moving! */
14068 int border_element_old[NUM_DIRECTIONS];
14071 for (i = 0; i < NUM_DIRECTIONS; i++)
14073 int xx = x + xy[i][0];
14074 int yy = y + xy[i][1];
14075 int border_element;
14077 border_element_old[i] = -1;
14079 if (!IN_LEV_FIELD(xx, yy))
14082 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14083 border_element = Feld[xx][yy]; /* may be moving! */
14084 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14085 border_element = Feld[xx][yy];
14086 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14087 border_element = MovingOrBlocked2Element(xx, yy);
14089 continue; /* center and border element do not touch */
14091 border_element_old[i] = border_element;
14094 for (i = 0; i < NUM_DIRECTIONS; i++)
14096 int xx = x + xy[i][0];
14097 int yy = y + xy[i][1];
14098 int center_side = trigger_sides[i][0];
14099 int border_element = border_element_old[i];
14101 if (border_element == -1)
14104 /* check for change of border element */
14105 CheckElementChangeBySide(xx, yy, border_element, center_element,
14106 CE_TOUCHING_X, center_side);
14108 /* (center element cannot be player, so we dont have to check this here) */
14111 for (i = 0; i < NUM_DIRECTIONS; i++)
14113 int xx = x + xy[i][0];
14114 int yy = y + xy[i][1];
14115 int border_side = trigger_sides[i][1];
14116 int border_element = border_element_old[i];
14118 if (border_element == -1)
14121 /* check for change of center element (but change it only once) */
14122 if (!change_center_element)
14123 change_center_element =
14124 CheckElementChangeBySide(x, y, center_element, border_element,
14125 CE_TOUCHING_X, border_side);
14127 #if USE_FIX_CE_ACTION_WITH_PLAYER
14128 if (IS_PLAYER(xx, yy))
14130 /* use player element that is initially defined in the level playfield,
14131 not the player element that corresponds to the runtime player number
14132 (example: a level that contains EL_PLAYER_3 as the only player would
14133 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14134 int player_element = PLAYERINFO(xx, yy)->initial_element;
14136 CheckElementChangeBySide(x, y, center_element, player_element,
14137 CE_TOUCHING_X, border_side);
14145 void TestIfElementTouchesCustomElement_OLD(int x, int y)
14147 static int xy[4][2] =
14154 static int trigger_sides[4][2] =
14156 /* center side border side */
14157 { CH_SIDE_TOP, CH_SIDE_BOTTOM }, /* check top */
14158 { CH_SIDE_LEFT, CH_SIDE_RIGHT }, /* check left */
14159 { CH_SIDE_RIGHT, CH_SIDE_LEFT }, /* check right */
14160 { CH_SIDE_BOTTOM, CH_SIDE_TOP } /* check bottom */
14162 static int touch_dir[4] =
14164 MV_LEFT | MV_RIGHT,
14169 boolean change_center_element = FALSE;
14170 int center_element = Feld[x][y]; /* should always be non-moving! */
14173 for (i = 0; i < NUM_DIRECTIONS; i++)
14175 int xx = x + xy[i][0];
14176 int yy = y + xy[i][1];
14177 int center_side = trigger_sides[i][0];
14178 int border_side = trigger_sides[i][1];
14179 int border_element;
14181 if (!IN_LEV_FIELD(xx, yy))
14184 if (game.engine_version < VERSION_IDENT(3,0,7,0))
14185 border_element = Feld[xx][yy]; /* may be moving! */
14186 else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
14187 border_element = Feld[xx][yy];
14188 else if (MovDir[xx][yy] & touch_dir[i]) /* elements are touching */
14189 border_element = MovingOrBlocked2Element(xx, yy);
14191 continue; /* center and border element do not touch */
14193 /* check for change of center element (but change it only once) */
14194 if (!change_center_element)
14195 change_center_element =
14196 CheckElementChangeBySide(x, y, center_element, border_element,
14197 CE_TOUCHING_X, border_side);
14199 /* check for change of border element */
14200 CheckElementChangeBySide(xx, yy, border_element, center_element,
14201 CE_TOUCHING_X, center_side);
14207 void TestIfElementHitsCustomElement(int x, int y, int direction)
14209 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14210 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14211 int hitx = x + dx, hity = y + dy;
14212 int hitting_element = Feld[x][y];
14213 int touched_element;
14215 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14218 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14219 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14221 if (IN_LEV_FIELD(hitx, hity))
14223 int opposite_direction = MV_DIR_OPPOSITE(direction);
14224 int hitting_side = direction;
14225 int touched_side = opposite_direction;
14226 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14227 MovDir[hitx][hity] != direction ||
14228 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14234 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14235 CE_HITTING_X, touched_side);
14237 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14238 CE_HIT_BY_X, hitting_side);
14240 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14241 CE_HIT_BY_SOMETHING, opposite_direction);
14243 #if USE_FIX_CE_ACTION_WITH_PLAYER
14244 if (IS_PLAYER(hitx, hity))
14246 /* use player element that is initially defined in the level playfield,
14247 not the player element that corresponds to the runtime player number
14248 (example: a level that contains EL_PLAYER_3 as the only player would
14249 incorrectly give EL_PLAYER_1 for "player->element_nr") */
14250 int player_element = PLAYERINFO(hitx, hity)->initial_element;
14252 CheckElementChangeBySide(x, y, hitting_element, player_element,
14253 CE_HITTING_X, touched_side);
14259 /* "hitting something" is also true when hitting the playfield border */
14260 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14261 CE_HITTING_SOMETHING, direction);
14265 void TestIfElementSmashesCustomElement(int x, int y, int direction)
14267 int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
14268 int dy = (direction == MV_UP ? -1 : direction == MV_DOWN ? +1 : 0);
14269 int hitx = x + dx, hity = y + dy;
14270 int hitting_element = Feld[x][y];
14271 int touched_element;
14273 boolean object_hit = (IN_LEV_FIELD(hitx, hity) &&
14274 !IS_FREE(hitx, hity) &&
14275 (!IS_MOVING(hitx, hity) ||
14276 MovDir[hitx][hity] != direction ||
14277 ABS(MovPos[hitx][hity]) <= TILEY / 2));
14280 if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
14284 if (IN_LEV_FIELD(hitx, hity) && !object_hit)
14288 touched_element = (IN_LEV_FIELD(hitx, hity) ?
14289 MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
14291 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14292 EP_CAN_SMASH_EVERYTHING, direction);
14294 if (IN_LEV_FIELD(hitx, hity))
14296 int opposite_direction = MV_DIR_OPPOSITE(direction);
14297 int hitting_side = direction;
14298 int touched_side = opposite_direction;
14300 int touched_element = MovingOrBlocked2Element(hitx, hity);
14303 boolean object_hit = (!IS_MOVING(hitx, hity) ||
14304 MovDir[hitx][hity] != direction ||
14305 ABS(MovPos[hitx][hity]) <= TILEY / 2);
14314 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14315 CE_SMASHED_BY_SOMETHING, opposite_direction);
14317 CheckElementChangeBySide(x, y, hitting_element, touched_element,
14318 CE_OTHER_IS_SMASHING, touched_side);
14320 CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
14321 CE_OTHER_GETS_SMASHED, hitting_side);
14327 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
14329 int i, kill_x = -1, kill_y = -1;
14331 int bad_element = -1;
14332 static int test_xy[4][2] =
14339 static int test_dir[4] =
14347 for (i = 0; i < NUM_DIRECTIONS; i++)
14349 int test_x, test_y, test_move_dir, test_element;
14351 test_x = good_x + test_xy[i][0];
14352 test_y = good_y + test_xy[i][1];
14354 if (!IN_LEV_FIELD(test_x, test_y))
14358 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14360 test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14362 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14363 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14365 if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14366 (DONT_TOUCH(test_element) && test_move_dir != test_dir[i]))
14370 bad_element = test_element;
14376 if (kill_x != -1 || kill_y != -1)
14378 if (IS_PLAYER(good_x, good_y))
14380 struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14382 if (player->shield_deadly_time_left > 0 &&
14383 !IS_INDESTRUCTIBLE(bad_element))
14384 Bang(kill_x, kill_y);
14385 else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14386 KillPlayer(player);
14389 Bang(good_x, good_y);
14393 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14395 int i, kill_x = -1, kill_y = -1;
14396 int bad_element = Feld[bad_x][bad_y];
14397 static int test_xy[4][2] =
14404 static int touch_dir[4] =
14406 MV_LEFT | MV_RIGHT,
14411 static int test_dir[4] =
14419 if (bad_element == EL_EXPLOSION) /* skip just exploding bad things */
14422 for (i = 0; i < NUM_DIRECTIONS; i++)
14424 int test_x, test_y, test_move_dir, test_element;
14426 test_x = bad_x + test_xy[i][0];
14427 test_y = bad_y + test_xy[i][1];
14429 if (!IN_LEV_FIELD(test_x, test_y))
14433 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14435 test_element = Feld[test_x][test_y];
14437 /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14438 2nd case: DONT_TOUCH style bad thing does not move away from good thing
14440 if ((DONT_RUN_INTO(bad_element) && bad_move_dir == test_dir[i]) ||
14441 (DONT_TOUCH(bad_element) && test_move_dir != test_dir[i]))
14443 /* good thing is player or penguin that does not move away */
14444 if (IS_PLAYER(test_x, test_y))
14446 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14448 if (bad_element == EL_ROBOT && player->is_moving)
14449 continue; /* robot does not kill player if he is moving */
14451 if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14453 if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14454 continue; /* center and border element do not touch */
14462 else if (test_element == EL_PENGUIN)
14472 if (kill_x != -1 || kill_y != -1)
14474 if (IS_PLAYER(kill_x, kill_y))
14476 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14478 if (player->shield_deadly_time_left > 0 &&
14479 !IS_INDESTRUCTIBLE(bad_element))
14480 Bang(bad_x, bad_y);
14481 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14482 KillPlayer(player);
14485 Bang(kill_x, kill_y);
14489 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14491 int bad_element = Feld[bad_x][bad_y];
14492 int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14493 int dy = (bad_move_dir == MV_UP ? -1 : bad_move_dir == MV_DOWN ? +1 : 0);
14494 int test_x = bad_x + dx, test_y = bad_y + dy;
14495 int test_move_dir, test_element;
14496 int kill_x = -1, kill_y = -1;
14498 if (!IN_LEV_FIELD(test_x, test_y))
14502 (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14504 test_element = Feld[test_x][test_y];
14506 if (test_move_dir != bad_move_dir)
14508 /* good thing can be player or penguin that does not move away */
14509 if (IS_PLAYER(test_x, test_y))
14511 struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14513 /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14514 player as being hit when he is moving towards the bad thing, because
14515 the "get hit by" condition would be lost after the player stops) */
14516 if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14517 return; /* player moves away from bad thing */
14522 else if (test_element == EL_PENGUIN)
14529 if (kill_x != -1 || kill_y != -1)
14531 if (IS_PLAYER(kill_x, kill_y))
14533 struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14535 if (player->shield_deadly_time_left > 0 &&
14536 !IS_INDESTRUCTIBLE(bad_element))
14537 Bang(bad_x, bad_y);
14538 else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14539 KillPlayer(player);
14542 Bang(kill_x, kill_y);
14546 void TestIfPlayerTouchesBadThing(int x, int y)
14548 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14551 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14553 TestIfGoodThingHitsBadThing(x, y, move_dir);
14556 void TestIfBadThingTouchesPlayer(int x, int y)
14558 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14561 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14563 TestIfBadThingHitsGoodThing(x, y, move_dir);
14566 void TestIfFriendTouchesBadThing(int x, int y)
14568 TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14571 void TestIfBadThingTouchesFriend(int x, int y)
14573 TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14576 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14578 int i, kill_x = bad_x, kill_y = bad_y;
14579 static int xy[4][2] =
14587 for (i = 0; i < NUM_DIRECTIONS; i++)
14591 x = bad_x + xy[i][0];
14592 y = bad_y + xy[i][1];
14593 if (!IN_LEV_FIELD(x, y))
14596 element = Feld[x][y];
14597 if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14598 element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14606 if (kill_x != bad_x || kill_y != bad_y)
14607 Bang(bad_x, bad_y);
14610 void KillPlayer(struct PlayerInfo *player)
14612 int jx = player->jx, jy = player->jy;
14614 if (!player->active)
14618 printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
14619 player->killed, player->active, player->reanimated);
14622 /* the following code was introduced to prevent an infinite loop when calling
14624 -> CheckTriggeredElementChangeExt()
14625 -> ExecuteCustomElementAction()
14627 -> (infinitely repeating the above sequence of function calls)
14628 which occurs when killing the player while having a CE with the setting
14629 "kill player X when explosion of <player X>"; the solution using a new
14630 field "player->killed" was chosen for backwards compatibility, although
14631 clever use of the fields "player->active" etc. would probably also work */
14633 if (player->killed)
14637 player->killed = TRUE;
14639 /* remove accessible field at the player's position */
14640 Feld[jx][jy] = EL_EMPTY;
14642 /* deactivate shield (else Bang()/Explode() would not work right) */
14643 player->shield_normal_time_left = 0;
14644 player->shield_deadly_time_left = 0;
14647 printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
14648 player->killed, player->active, player->reanimated);
14654 printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
14655 player->killed, player->active, player->reanimated);
14658 #if USE_PLAYER_REANIMATION
14660 if (player->reanimated) /* killed player may have been reanimated */
14661 player->killed = player->reanimated = FALSE;
14663 BuryPlayer(player);
14665 if (player->killed) /* player may have been reanimated */
14666 BuryPlayer(player);
14669 BuryPlayer(player);
14673 static void KillPlayerUnlessEnemyProtected(int x, int y)
14675 if (!PLAYER_ENEMY_PROTECTED(x, y))
14676 KillPlayer(PLAYERINFO(x, y));
14679 static void KillPlayerUnlessExplosionProtected(int x, int y)
14681 if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14682 KillPlayer(PLAYERINFO(x, y));
14685 void BuryPlayer(struct PlayerInfo *player)
14687 int jx = player->jx, jy = player->jy;
14689 if (!player->active)
14692 PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14693 PlayLevelSound(jx, jy, SND_GAME_LOSING);
14695 player->GameOver = TRUE;
14696 RemovePlayer(player);
14699 void RemovePlayer(struct PlayerInfo *player)
14701 int jx = player->jx, jy = player->jy;
14702 int i, found = FALSE;
14704 player->present = FALSE;
14705 player->active = FALSE;
14707 if (!ExplodeField[jx][jy])
14708 StorePlayer[jx][jy] = 0;
14710 if (player->is_moving)
14711 TEST_DrawLevelField(player->last_jx, player->last_jy);
14713 for (i = 0; i < MAX_PLAYERS; i++)
14714 if (stored_player[i].active)
14718 AllPlayersGone = TRUE;
14724 #if USE_NEW_SNAP_DELAY
14725 static void setFieldForSnapping(int x, int y, int element, int direction)
14727 struct ElementInfo *ei = &element_info[element];
14728 int direction_bit = MV_DIR_TO_BIT(direction);
14729 int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14730 int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14731 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14733 Feld[x][y] = EL_ELEMENT_SNAPPING;
14734 MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14736 ResetGfxAnimation(x, y);
14738 GfxElement[x][y] = element;
14739 GfxAction[x][y] = action;
14740 GfxDir[x][y] = direction;
14741 GfxFrame[x][y] = -1;
14746 =============================================================================
14747 checkDiagonalPushing()
14748 -----------------------------------------------------------------------------
14749 check if diagonal input device direction results in pushing of object
14750 (by checking if the alternative direction is walkable, diggable, ...)
14751 =============================================================================
14754 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14755 int x, int y, int real_dx, int real_dy)
14757 int jx, jy, dx, dy, xx, yy;
14759 if (real_dx == 0 || real_dy == 0) /* no diagonal direction => push */
14762 /* diagonal direction: check alternative direction */
14767 xx = jx + (dx == 0 ? real_dx : 0);
14768 yy = jy + (dy == 0 ? real_dy : 0);
14770 return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
14774 =============================================================================
14776 -----------------------------------------------------------------------------
14777 x, y: field next to player (non-diagonal) to try to dig to
14778 real_dx, real_dy: direction as read from input device (can be diagonal)
14779 =============================================================================
14782 static int DigField(struct PlayerInfo *player,
14783 int oldx, int oldy, int x, int y,
14784 int real_dx, int real_dy, int mode)
14786 boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14787 boolean player_was_pushing = player->is_pushing;
14788 boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14789 boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14790 int jx = oldx, jy = oldy;
14791 int dx = x - jx, dy = y - jy;
14792 int nextx = x + dx, nexty = y + dy;
14793 int move_direction = (dx == -1 ? MV_LEFT :
14794 dx == +1 ? MV_RIGHT :
14796 dy == +1 ? MV_DOWN : MV_NONE);
14797 int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14798 int dig_side = MV_DIR_OPPOSITE(move_direction);
14799 int old_element = Feld[jx][jy];
14800 #if USE_FIXED_DONT_RUN_INTO
14801 int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14807 if (is_player) /* function can also be called by EL_PENGUIN */
14809 if (player->MovPos == 0)
14811 player->is_digging = FALSE;
14812 player->is_collecting = FALSE;
14815 if (player->MovPos == 0) /* last pushing move finished */
14816 player->is_pushing = FALSE;
14818 if (mode == DF_NO_PUSH) /* player just stopped pushing */
14820 player->is_switching = FALSE;
14821 player->push_delay = -1;
14823 return MP_NO_ACTION;
14827 #if !USE_FIXED_DONT_RUN_INTO
14828 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14829 return MP_NO_ACTION;
14832 if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14833 old_element = Back[jx][jy];
14835 /* in case of element dropped at player position, check background */
14836 else if (Back[jx][jy] != EL_EMPTY &&
14837 game.engine_version >= VERSION_IDENT(2,2,0,0))
14838 old_element = Back[jx][jy];
14840 if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14841 return MP_NO_ACTION; /* field has no opening in this direction */
14843 if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14844 return MP_NO_ACTION; /* field has no opening in this direction */
14846 #if USE_FIXED_DONT_RUN_INTO
14847 if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14851 Feld[jx][jy] = player->artwork_element;
14852 InitMovingField(jx, jy, MV_DOWN);
14853 Store[jx][jy] = EL_ACID;
14854 ContinueMoving(jx, jy);
14855 BuryPlayer(player);
14857 return MP_DONT_RUN_INTO;
14861 #if USE_FIXED_DONT_RUN_INTO
14862 if (player_can_move && DONT_RUN_INTO(element))
14864 TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14866 return MP_DONT_RUN_INTO;
14870 #if USE_FIXED_DONT_RUN_INTO
14871 if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14872 return MP_NO_ACTION;
14875 #if !USE_FIXED_DONT_RUN_INTO
14876 element = Feld[x][y];
14879 collect_count = element_info[element].collect_count_initial;
14881 if (!is_player && !IS_COLLECTIBLE(element)) /* penguin cannot collect it */
14882 return MP_NO_ACTION;
14884 if (game.engine_version < VERSION_IDENT(2,2,0,0))
14885 player_can_move = player_can_move_or_snap;
14887 if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14888 game.engine_version >= VERSION_IDENT(2,2,0,0))
14890 CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14891 player->index_bit, dig_side);
14892 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14893 player->index_bit, dig_side);
14895 if (element == EL_DC_LANDMINE)
14898 if (Feld[x][y] != element) /* field changed by snapping */
14901 return MP_NO_ACTION;
14904 #if USE_PLAYER_GRAVITY
14905 if (player->gravity && is_player && !player->is_auto_moving &&
14906 canFallDown(player) && move_direction != MV_DOWN &&
14907 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14908 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14910 if (game.gravity && is_player && !player->is_auto_moving &&
14911 canFallDown(player) && move_direction != MV_DOWN &&
14912 !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14913 return MP_NO_ACTION; /* player cannot walk here due to gravity */
14916 if (player_can_move &&
14917 IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14919 int sound_element = SND_ELEMENT(element);
14920 int sound_action = ACTION_WALKING;
14922 if (IS_RND_GATE(element))
14924 if (!player->key[RND_GATE_NR(element)])
14925 return MP_NO_ACTION;
14927 else if (IS_RND_GATE_GRAY(element))
14929 if (!player->key[RND_GATE_GRAY_NR(element)])
14930 return MP_NO_ACTION;
14932 else if (IS_RND_GATE_GRAY_ACTIVE(element))
14934 if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14935 return MP_NO_ACTION;
14937 else if (element == EL_EXIT_OPEN ||
14938 element == EL_EM_EXIT_OPEN ||
14940 element == EL_EM_EXIT_OPENING ||
14942 element == EL_STEEL_EXIT_OPEN ||
14943 element == EL_EM_STEEL_EXIT_OPEN ||
14945 element == EL_EM_STEEL_EXIT_OPENING ||
14947 element == EL_SP_EXIT_OPEN ||
14948 element == EL_SP_EXIT_OPENING)
14950 sound_action = ACTION_PASSING; /* player is passing exit */
14952 else if (element == EL_EMPTY)
14954 sound_action = ACTION_MOVING; /* nothing to walk on */
14957 /* play sound from background or player, whatever is available */
14958 if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14959 PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14961 PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14963 else if (player_can_move &&
14964 IS_PASSABLE(element) && canPassField(x, y, move_direction))
14966 if (!ACCESS_FROM(element, opposite_direction))
14967 return MP_NO_ACTION; /* field not accessible from this direction */
14969 if (CAN_MOVE(element)) /* only fixed elements can be passed! */
14970 return MP_NO_ACTION;
14972 if (IS_EM_GATE(element))
14974 if (!player->key[EM_GATE_NR(element)])
14975 return MP_NO_ACTION;
14977 else if (IS_EM_GATE_GRAY(element))
14979 if (!player->key[EM_GATE_GRAY_NR(element)])
14980 return MP_NO_ACTION;
14982 else if (IS_EM_GATE_GRAY_ACTIVE(element))
14984 if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14985 return MP_NO_ACTION;
14987 else if (IS_EMC_GATE(element))
14989 if (!player->key[EMC_GATE_NR(element)])
14990 return MP_NO_ACTION;
14992 else if (IS_EMC_GATE_GRAY(element))
14994 if (!player->key[EMC_GATE_GRAY_NR(element)])
14995 return MP_NO_ACTION;
14997 else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14999 if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
15000 return MP_NO_ACTION;
15002 else if (element == EL_DC_GATE_WHITE ||
15003 element == EL_DC_GATE_WHITE_GRAY ||
15004 element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
15006 if (player->num_white_keys == 0)
15007 return MP_NO_ACTION;
15009 player->num_white_keys--;
15011 else if (IS_SP_PORT(element))
15013 if (element == EL_SP_GRAVITY_PORT_LEFT ||
15014 element == EL_SP_GRAVITY_PORT_RIGHT ||
15015 element == EL_SP_GRAVITY_PORT_UP ||
15016 element == EL_SP_GRAVITY_PORT_DOWN)
15017 #if USE_PLAYER_GRAVITY
15018 player->gravity = !player->gravity;
15020 game.gravity = !game.gravity;
15022 else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
15023 element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
15024 element == EL_SP_GRAVITY_ON_PORT_UP ||
15025 element == EL_SP_GRAVITY_ON_PORT_DOWN)
15026 #if USE_PLAYER_GRAVITY
15027 player->gravity = TRUE;
15029 game.gravity = TRUE;
15031 else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
15032 element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
15033 element == EL_SP_GRAVITY_OFF_PORT_UP ||
15034 element == EL_SP_GRAVITY_OFF_PORT_DOWN)
15035 #if USE_PLAYER_GRAVITY
15036 player->gravity = FALSE;
15038 game.gravity = FALSE;
15042 /* automatically move to the next field with double speed */
15043 player->programmed_action = move_direction;
15045 if (player->move_delay_reset_counter == 0)
15047 player->move_delay_reset_counter = 2; /* two double speed steps */
15049 DOUBLE_PLAYER_SPEED(player);
15052 PlayLevelSoundAction(x, y, ACTION_PASSING);
15054 else if (player_can_move_or_snap && IS_DIGGABLE(element))
15058 if (mode != DF_SNAP)
15060 GfxElement[x][y] = GFX_ELEMENT(element);
15061 player->is_digging = TRUE;
15064 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15066 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
15067 player->index_bit, dig_side);
15069 if (mode == DF_SNAP)
15071 #if USE_NEW_SNAP_DELAY
15072 if (level.block_snap_field)
15073 setFieldForSnapping(x, y, element, move_direction);
15075 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15077 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15080 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15081 player->index_bit, dig_side);
15084 else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
15088 if (is_player && mode != DF_SNAP)
15090 GfxElement[x][y] = element;
15091 player->is_collecting = TRUE;
15094 if (element == EL_SPEED_PILL)
15096 player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
15098 else if (element == EL_EXTRA_TIME && level.time > 0)
15100 TimeLeft += level.extra_time;
15103 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15105 DisplayGameControlValues();
15107 DrawGameValue_Time(TimeLeft);
15110 else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
15112 player->shield_normal_time_left += level.shield_normal_time;
15113 if (element == EL_SHIELD_DEADLY)
15114 player->shield_deadly_time_left += level.shield_deadly_time;
15116 else if (element == EL_DYNAMITE ||
15117 element == EL_EM_DYNAMITE ||
15118 element == EL_SP_DISK_RED)
15120 if (player->inventory_size < MAX_INVENTORY_SIZE)
15121 player->inventory_element[player->inventory_size++] = element;
15123 DrawGameDoorValues();
15125 else if (element == EL_DYNABOMB_INCREASE_NUMBER)
15127 player->dynabomb_count++;
15128 player->dynabombs_left++;
15130 else if (element == EL_DYNABOMB_INCREASE_SIZE)
15132 player->dynabomb_size++;
15134 else if (element == EL_DYNABOMB_INCREASE_POWER)
15136 player->dynabomb_xl = TRUE;
15138 else if (IS_KEY(element))
15140 player->key[KEY_NR(element)] = TRUE;
15142 DrawGameDoorValues();
15144 else if (element == EL_DC_KEY_WHITE)
15146 player->num_white_keys++;
15148 /* display white keys? */
15149 /* DrawGameDoorValues(); */
15151 else if (IS_ENVELOPE(element))
15153 player->show_envelope = element;
15155 else if (element == EL_EMC_LENSES)
15157 game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
15159 RedrawAllInvisibleElementsForLenses();
15161 else if (element == EL_EMC_MAGNIFIER)
15163 game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
15165 RedrawAllInvisibleElementsForMagnifier();
15167 else if (IS_DROPPABLE(element) ||
15168 IS_THROWABLE(element)) /* can be collected and dropped */
15172 if (collect_count == 0)
15173 player->inventory_infinite_element = element;
15175 for (i = 0; i < collect_count; i++)
15176 if (player->inventory_size < MAX_INVENTORY_SIZE)
15177 player->inventory_element[player->inventory_size++] = element;
15179 DrawGameDoorValues();
15181 else if (collect_count > 0)
15183 local_player->gems_still_needed -= collect_count;
15184 if (local_player->gems_still_needed < 0)
15185 local_player->gems_still_needed = 0;
15188 game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
15190 DisplayGameControlValues();
15192 DrawGameValue_Emeralds(local_player->gems_still_needed);
15196 RaiseScoreElement(element);
15197 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15200 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
15201 player->index_bit, dig_side);
15203 if (mode == DF_SNAP)
15205 #if USE_NEW_SNAP_DELAY
15206 if (level.block_snap_field)
15207 setFieldForSnapping(x, y, element, move_direction);
15209 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15211 TestIfElementTouchesCustomElement(x, y); /* for empty space */
15214 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
15215 player->index_bit, dig_side);
15218 else if (player_can_move_or_snap && IS_PUSHABLE(element))
15220 if (mode == DF_SNAP && element != EL_BD_ROCK)
15221 return MP_NO_ACTION;
15223 if (CAN_FALL(element) && dy)
15224 return MP_NO_ACTION;
15226 if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
15227 !(element == EL_SPRING && level.use_spring_bug))
15228 return MP_NO_ACTION;
15230 if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
15231 ((move_direction & MV_VERTICAL &&
15232 ((element_info[element].move_pattern & MV_LEFT &&
15233 IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
15234 (element_info[element].move_pattern & MV_RIGHT &&
15235 IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
15236 (move_direction & MV_HORIZONTAL &&
15237 ((element_info[element].move_pattern & MV_UP &&
15238 IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
15239 (element_info[element].move_pattern & MV_DOWN &&
15240 IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
15241 return MP_NO_ACTION;
15243 /* do not push elements already moving away faster than player */
15244 if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
15245 ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
15246 return MP_NO_ACTION;
15248 if (game.engine_version >= VERSION_IDENT(3,1,0,0))
15250 if (player->push_delay_value == -1 || !player_was_pushing)
15251 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15253 else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15255 if (player->push_delay_value == -1)
15256 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15258 else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
15260 if (!player->is_pushing)
15261 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15264 player->is_pushing = TRUE;
15265 player->is_active = TRUE;
15267 if (!(IN_LEV_FIELD(nextx, nexty) &&
15268 (IS_FREE(nextx, nexty) ||
15269 (IS_SB_ELEMENT(element) &&
15270 Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
15271 (IS_CUSTOM_ELEMENT(element) &&
15272 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
15273 return MP_NO_ACTION;
15275 if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
15276 return MP_NO_ACTION;
15278 if (player->push_delay == -1) /* new pushing; restart delay */
15279 player->push_delay = 0;
15281 if (player->push_delay < player->push_delay_value &&
15282 !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
15283 element != EL_SPRING && element != EL_BALLOON)
15285 /* make sure that there is no move delay before next try to push */
15286 if (game.engine_version >= VERSION_IDENT(3,0,7,1))
15287 player->move_delay = 0;
15289 return MP_NO_ACTION;
15292 if (IS_CUSTOM_ELEMENT(element) &&
15293 CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
15295 if (!DigFieldByCE(nextx, nexty, element))
15296 return MP_NO_ACTION;
15299 if (IS_SB_ELEMENT(element))
15301 if (element == EL_SOKOBAN_FIELD_FULL)
15303 Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
15304 local_player->sokobanfields_still_needed++;
15307 if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
15309 Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
15310 local_player->sokobanfields_still_needed--;
15313 Feld[x][y] = EL_SOKOBAN_OBJECT;
15315 if (Back[x][y] == Back[nextx][nexty])
15316 PlayLevelSoundAction(x, y, ACTION_PUSHING);
15317 else if (Back[x][y] != 0)
15318 PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
15321 PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
15324 if (local_player->sokobanfields_still_needed == 0 &&
15325 game.emulation == EMU_SOKOBAN)
15327 PlayerWins(player);
15329 PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
15333 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15335 InitMovingField(x, y, move_direction);
15336 GfxAction[x][y] = ACTION_PUSHING;
15338 if (mode == DF_SNAP)
15339 ContinueMoving(x, y);
15341 MovPos[x][y] = (dx != 0 ? dx : dy);
15343 Pushed[x][y] = TRUE;
15344 Pushed[nextx][nexty] = TRUE;
15346 if (game.engine_version < VERSION_IDENT(2,2,0,7))
15347 player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15349 player->push_delay_value = -1; /* get new value later */
15351 /* check for element change _after_ element has been pushed */
15352 if (game.use_change_when_pushing_bug)
15354 CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15355 player->index_bit, dig_side);
15356 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15357 player->index_bit, dig_side);
15360 else if (IS_SWITCHABLE(element))
15362 if (PLAYER_SWITCHING(player, x, y))
15364 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15365 player->index_bit, dig_side);
15370 player->is_switching = TRUE;
15371 player->switch_x = x;
15372 player->switch_y = y;
15374 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15376 if (element == EL_ROBOT_WHEEL)
15378 Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15382 game.robot_wheel_active = TRUE;
15384 TEST_DrawLevelField(x, y);
15386 else if (element == EL_SP_TERMINAL)
15390 SCAN_PLAYFIELD(xx, yy)
15392 if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
15394 else if (Feld[xx][yy] == EL_SP_TERMINAL)
15395 Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15398 else if (IS_BELT_SWITCH(element))
15400 ToggleBeltSwitch(x, y);
15402 else if (element == EL_SWITCHGATE_SWITCH_UP ||
15403 element == EL_SWITCHGATE_SWITCH_DOWN ||
15404 element == EL_DC_SWITCHGATE_SWITCH_UP ||
15405 element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15407 ToggleSwitchgateSwitch(x, y);
15409 else if (element == EL_LIGHT_SWITCH ||
15410 element == EL_LIGHT_SWITCH_ACTIVE)
15412 ToggleLightSwitch(x, y);
15414 else if (element == EL_TIMEGATE_SWITCH ||
15415 element == EL_DC_TIMEGATE_SWITCH)
15417 ActivateTimegateSwitch(x, y);
15419 else if (element == EL_BALLOON_SWITCH_LEFT ||
15420 element == EL_BALLOON_SWITCH_RIGHT ||
15421 element == EL_BALLOON_SWITCH_UP ||
15422 element == EL_BALLOON_SWITCH_DOWN ||
15423 element == EL_BALLOON_SWITCH_NONE ||
15424 element == EL_BALLOON_SWITCH_ANY)
15426 game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT ? MV_LEFT :
15427 element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15428 element == EL_BALLOON_SWITCH_UP ? MV_UP :
15429 element == EL_BALLOON_SWITCH_DOWN ? MV_DOWN :
15430 element == EL_BALLOON_SWITCH_NONE ? MV_NONE :
15433 else if (element == EL_LAMP)
15435 Feld[x][y] = EL_LAMP_ACTIVE;
15436 local_player->lights_still_needed--;
15438 ResetGfxAnimation(x, y);
15439 TEST_DrawLevelField(x, y);
15441 else if (element == EL_TIME_ORB_FULL)
15443 Feld[x][y] = EL_TIME_ORB_EMPTY;
15445 if (level.time > 0 || level.use_time_orb_bug)
15447 TimeLeft += level.time_orb_time;
15450 game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15452 DisplayGameControlValues();
15454 DrawGameValue_Time(TimeLeft);
15458 ResetGfxAnimation(x, y);
15459 TEST_DrawLevelField(x, y);
15461 else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15462 element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15466 game.ball_state = !game.ball_state;
15468 SCAN_PLAYFIELD(xx, yy)
15470 int e = Feld[xx][yy];
15472 if (game.ball_state)
15474 if (e == EL_EMC_MAGIC_BALL)
15475 CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15476 else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15477 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15481 if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15482 CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15483 else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15484 CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15489 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15490 player->index_bit, dig_side);
15492 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15493 player->index_bit, dig_side);
15495 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15496 player->index_bit, dig_side);
15502 if (!PLAYER_SWITCHING(player, x, y))
15504 player->is_switching = TRUE;
15505 player->switch_x = x;
15506 player->switch_y = y;
15508 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15509 player->index_bit, dig_side);
15510 CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15511 player->index_bit, dig_side);
15513 CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15514 player->index_bit, dig_side);
15515 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15516 player->index_bit, dig_side);
15519 CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15520 player->index_bit, dig_side);
15521 CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15522 player->index_bit, dig_side);
15524 return MP_NO_ACTION;
15527 player->push_delay = -1;
15529 if (is_player) /* function can also be called by EL_PENGUIN */
15531 if (Feld[x][y] != element) /* really digged/collected something */
15533 player->is_collecting = !player->is_digging;
15534 player->is_active = TRUE;
15541 static boolean DigFieldByCE(int x, int y, int digging_element)
15543 int element = Feld[x][y];
15545 if (!IS_FREE(x, y))
15547 int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15548 IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15551 /* no element can dig solid indestructible elements */
15552 if (IS_INDESTRUCTIBLE(element) &&
15553 !IS_DIGGABLE(element) &&
15554 !IS_COLLECTIBLE(element))
15557 if (AmoebaNr[x][y] &&
15558 (element == EL_AMOEBA_FULL ||
15559 element == EL_BD_AMOEBA ||
15560 element == EL_AMOEBA_GROWING))
15562 AmoebaCnt[AmoebaNr[x][y]]--;
15563 AmoebaCnt2[AmoebaNr[x][y]]--;
15566 if (IS_MOVING(x, y))
15567 RemoveMovingField(x, y);
15571 TEST_DrawLevelField(x, y);
15574 /* if digged element was about to explode, prevent the explosion */
15575 ExplodeField[x][y] = EX_TYPE_NONE;
15577 PlayLevelSoundAction(x, y, action);
15580 Store[x][y] = EL_EMPTY;
15583 /* this makes it possible to leave the removed element again */
15584 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15585 Store[x][y] = element;
15587 if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15589 int move_leave_element = element_info[digging_element].move_leave_element;
15591 /* this makes it possible to leave the removed element again */
15592 Store[x][y] = (move_leave_element == EL_TRIGGER_ELEMENT ?
15593 element : move_leave_element);
15600 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15602 int jx = player->jx, jy = player->jy;
15603 int x = jx + dx, y = jy + dy;
15604 int snap_direction = (dx == -1 ? MV_LEFT :
15605 dx == +1 ? MV_RIGHT :
15607 dy == +1 ? MV_DOWN : MV_NONE);
15608 boolean can_continue_snapping = (level.continuous_snapping &&
15609 WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15611 if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15614 if (!player->active || !IN_LEV_FIELD(x, y))
15622 if (player->MovPos == 0)
15623 player->is_pushing = FALSE;
15625 player->is_snapping = FALSE;
15627 if (player->MovPos == 0)
15629 player->is_moving = FALSE;
15630 player->is_digging = FALSE;
15631 player->is_collecting = FALSE;
15637 #if USE_NEW_CONTINUOUS_SNAPPING
15638 /* prevent snapping with already pressed snap key when not allowed */
15639 if (player->is_snapping && !can_continue_snapping)
15642 if (player->is_snapping)
15646 player->MovDir = snap_direction;
15648 if (player->MovPos == 0)
15650 player->is_moving = FALSE;
15651 player->is_digging = FALSE;
15652 player->is_collecting = FALSE;
15655 player->is_dropping = FALSE;
15656 player->is_dropping_pressed = FALSE;
15657 player->drop_pressed_delay = 0;
15659 if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15662 player->is_snapping = TRUE;
15663 player->is_active = TRUE;
15665 if (player->MovPos == 0)
15667 player->is_moving = FALSE;
15668 player->is_digging = FALSE;
15669 player->is_collecting = FALSE;
15672 if (player->MovPos != 0) /* prevent graphic bugs in versions < 2.2.0 */
15673 TEST_DrawLevelField(player->last_jx, player->last_jy);
15675 TEST_DrawLevelField(x, y);
15680 static boolean DropElement(struct PlayerInfo *player)
15682 int old_element, new_element;
15683 int dropx = player->jx, dropy = player->jy;
15684 int drop_direction = player->MovDir;
15685 int drop_side = drop_direction;
15687 int drop_element = get_next_dropped_element(player);
15689 int drop_element = (player->inventory_size > 0 ?
15690 player->inventory_element[player->inventory_size - 1] :
15691 player->inventory_infinite_element != EL_UNDEFINED ?
15692 player->inventory_infinite_element :
15693 player->dynabombs_left > 0 ?
15694 EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
15698 player->is_dropping_pressed = TRUE;
15700 /* do not drop an element on top of another element; when holding drop key
15701 pressed without moving, dropped element must move away before the next
15702 element can be dropped (this is especially important if the next element
15703 is dynamite, which can be placed on background for historical reasons) */
15704 if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
15707 if (IS_THROWABLE(drop_element))
15709 dropx += GET_DX_FROM_DIR(drop_direction);
15710 dropy += GET_DY_FROM_DIR(drop_direction);
15712 if (!IN_LEV_FIELD(dropx, dropy))
15716 old_element = Feld[dropx][dropy]; /* old element at dropping position */
15717 new_element = drop_element; /* default: no change when dropping */
15719 /* check if player is active, not moving and ready to drop */
15720 if (!player->active || player->MovPos || player->drop_delay > 0)
15723 /* check if player has anything that can be dropped */
15724 if (new_element == EL_UNDEFINED)
15727 /* check if drop key was pressed long enough for EM style dynamite */
15728 if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15731 /* check if anything can be dropped at the current position */
15732 if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15735 /* collected custom elements can only be dropped on empty fields */
15736 if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15739 if (old_element != EL_EMPTY)
15740 Back[dropx][dropy] = old_element; /* store old element on this field */
15742 ResetGfxAnimation(dropx, dropy);
15743 ResetRandomAnimationValue(dropx, dropy);
15745 if (player->inventory_size > 0 ||
15746 player->inventory_infinite_element != EL_UNDEFINED)
15748 if (player->inventory_size > 0)
15750 player->inventory_size--;
15752 DrawGameDoorValues();
15754 if (new_element == EL_DYNAMITE)
15755 new_element = EL_DYNAMITE_ACTIVE;
15756 else if (new_element == EL_EM_DYNAMITE)
15757 new_element = EL_EM_DYNAMITE_ACTIVE;
15758 else if (new_element == EL_SP_DISK_RED)
15759 new_element = EL_SP_DISK_RED_ACTIVE;
15762 Feld[dropx][dropy] = new_element;
15764 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15765 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15766 el2img(Feld[dropx][dropy]), 0);
15768 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15770 /* needed if previous element just changed to "empty" in the last frame */
15771 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15773 CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15774 player->index_bit, drop_side);
15775 CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15777 player->index_bit, drop_side);
15779 TestIfElementTouchesCustomElement(dropx, dropy);
15781 else /* player is dropping a dyna bomb */
15783 player->dynabombs_left--;
15785 Feld[dropx][dropy] = new_element;
15787 if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15788 DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15789 el2img(Feld[dropx][dropy]), 0);
15791 PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15794 if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
15795 InitField_WithBug1(dropx, dropy, FALSE);
15797 new_element = Feld[dropx][dropy]; /* element might have changed */
15799 if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15800 element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15802 int move_direction, nextx, nexty;
15804 if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15805 MovDir[dropx][dropy] = drop_direction;
15807 move_direction = MovDir[dropx][dropy];
15808 nextx = dropx + GET_DX_FROM_DIR(move_direction);
15809 nexty = dropy + GET_DY_FROM_DIR(move_direction);
15811 ChangeCount[dropx][dropy] = 0; /* allow at least one more change */
15813 #if USE_FIX_IMPACT_COLLISION
15814 /* do not cause impact style collision by dropping elements that can fall */
15815 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15817 CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15821 player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15822 player->is_dropping = TRUE;
15824 player->drop_pressed_delay = 0;
15825 player->is_dropping_pressed = FALSE;
15827 player->drop_x = dropx;
15828 player->drop_y = dropy;
15833 /* ------------------------------------------------------------------------- */
15834 /* game sound playing functions */
15835 /* ------------------------------------------------------------------------- */
15837 static int *loop_sound_frame = NULL;
15838 static int *loop_sound_volume = NULL;
15840 void InitPlayLevelSound()
15842 int num_sounds = getSoundListSize();
15844 checked_free(loop_sound_frame);
15845 checked_free(loop_sound_volume);
15847 loop_sound_frame = checked_calloc(num_sounds * sizeof(int));
15848 loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15851 static void PlayLevelSound(int x, int y, int nr)
15853 int sx = SCREENX(x), sy = SCREENY(y);
15854 int volume, stereo_position;
15855 int max_distance = 8;
15856 int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15858 if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15859 (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15862 if (!IN_LEV_FIELD(x, y) ||
15863 sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15864 sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15867 volume = SOUND_MAX_VOLUME;
15869 if (!IN_SCR_FIELD(sx, sy))
15871 int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15872 int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15874 volume -= volume * (dx > dy ? dx : dy) / max_distance;
15877 stereo_position = (SOUND_MAX_LEFT +
15878 (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15879 (SCR_FIELDX + 2 * max_distance));
15881 if (IS_LOOP_SOUND(nr))
15883 /* This assures that quieter loop sounds do not overwrite louder ones,
15884 while restarting sound volume comparison with each new game frame. */
15886 if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15889 loop_sound_volume[nr] = volume;
15890 loop_sound_frame[nr] = FrameCounter;
15893 PlaySoundExt(nr, volume, stereo_position, type);
15896 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15898 PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15899 x > LEVELX(BX2) ? LEVELX(BX2) : x,
15900 y < LEVELY(BY1) ? LEVELY(BY1) :
15901 y > LEVELY(BY2) ? LEVELY(BY2) : y,
15905 static void PlayLevelSoundAction(int x, int y, int action)
15907 PlayLevelSoundElementAction(x, y, Feld[x][y], action);
15910 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15912 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15914 if (sound_effect != SND_UNDEFINED)
15915 PlayLevelSound(x, y, sound_effect);
15918 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15921 int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15923 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15924 PlayLevelSound(x, y, sound_effect);
15927 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15929 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15931 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15932 PlayLevelSound(x, y, sound_effect);
15935 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15937 int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
15939 if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15940 StopSound(sound_effect);
15943 static void PlayLevelMusic()
15945 if (levelset.music[level_nr] != MUS_UNDEFINED)
15946 PlayMusic(levelset.music[level_nr]); /* from config file */
15948 PlayMusic(MAP_NOCONF_MUSIC(level_nr)); /* from music dir */
15951 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15953 int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
15954 int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
15955 int x = xx - 1 - offset;
15956 int y = yy - 1 - offset;
15961 PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15965 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15969 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15973 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15977 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15981 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15985 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15988 case SAMPLE_android_clone:
15989 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15992 case SAMPLE_android_move:
15993 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15996 case SAMPLE_spring:
15997 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16001 PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16005 PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16008 case SAMPLE_eater_eat:
16009 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16013 PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16016 case SAMPLE_collect:
16017 PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16020 case SAMPLE_diamond:
16021 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16024 case SAMPLE_squash:
16025 /* !!! CHECK THIS !!! */
16027 PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16029 PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16033 case SAMPLE_wonderfall:
16034 PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16038 PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16042 PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16046 PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16050 PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16054 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16058 PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16061 case SAMPLE_wonder:
16062 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16066 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16069 case SAMPLE_exit_open:
16070 PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16073 case SAMPLE_exit_leave:
16074 PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16077 case SAMPLE_dynamite:
16078 PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16082 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16086 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16090 PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16094 PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16098 PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16102 PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16106 PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16112 void ChangeTime(int value)
16114 int *time = (level.time == 0 ? &TimePlayed : &TimeLeft);
16118 /* EMC game engine uses value from time counter of RND game engine */
16119 level.native_em_level->lev->time = *time;
16121 DrawGameValue_Time(*time);
16124 void RaiseScore(int value)
16126 /* EMC game engine and RND game engine have separate score counters */
16127 int *score = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
16128 &level.native_em_level->lev->score : &local_player->score);
16132 DrawGameValue_Score(*score);
16136 void RaiseScore(int value)
16138 local_player->score += value;
16141 game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
16143 DisplayGameControlValues();
16145 DrawGameValue_Score(local_player->score);
16149 void RaiseScoreElement(int element)
16154 case EL_BD_DIAMOND:
16155 case EL_EMERALD_YELLOW:
16156 case EL_EMERALD_RED:
16157 case EL_EMERALD_PURPLE:
16158 case EL_SP_INFOTRON:
16159 RaiseScore(level.score[SC_EMERALD]);
16162 RaiseScore(level.score[SC_DIAMOND]);
16165 RaiseScore(level.score[SC_CRYSTAL]);
16168 RaiseScore(level.score[SC_PEARL]);
16171 case EL_BD_BUTTERFLY:
16172 case EL_SP_ELECTRON:
16173 RaiseScore(level.score[SC_BUG]);
16176 case EL_BD_FIREFLY:
16177 case EL_SP_SNIKSNAK:
16178 RaiseScore(level.score[SC_SPACESHIP]);
16181 case EL_DARK_YAMYAM:
16182 RaiseScore(level.score[SC_YAMYAM]);
16185 RaiseScore(level.score[SC_ROBOT]);
16188 RaiseScore(level.score[SC_PACMAN]);
16191 RaiseScore(level.score[SC_NUT]);
16194 case EL_EM_DYNAMITE:
16195 case EL_SP_DISK_RED:
16196 case EL_DYNABOMB_INCREASE_NUMBER:
16197 case EL_DYNABOMB_INCREASE_SIZE:
16198 case EL_DYNABOMB_INCREASE_POWER:
16199 RaiseScore(level.score[SC_DYNAMITE]);
16201 case EL_SHIELD_NORMAL:
16202 case EL_SHIELD_DEADLY:
16203 RaiseScore(level.score[SC_SHIELD]);
16205 case EL_EXTRA_TIME:
16206 RaiseScore(level.extra_time_score);
16220 case EL_DC_KEY_WHITE:
16221 RaiseScore(level.score[SC_KEY]);
16224 RaiseScore(element_info[element].collect_score);
16229 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16231 if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16233 #if defined(NETWORK_AVALIABLE)
16234 if (options.network)
16235 SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16244 FadeSkipNextFadeIn();
16246 fading = fading_none;
16250 OpenDoor(DOOR_CLOSE_1);
16253 game_status = GAME_MODE_MAIN;
16256 DrawAndFadeInMainMenu(REDRAW_FIELD);
16264 FadeOut(REDRAW_FIELD);
16267 game_status = GAME_MODE_MAIN;
16269 DrawAndFadeInMainMenu(REDRAW_FIELD);
16273 else /* continue playing the game */
16275 if (tape.playing && tape.deactivate_display)
16276 TapeDeactivateDisplayOff(TRUE);
16278 OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16280 if (tape.playing && tape.deactivate_display)
16281 TapeDeactivateDisplayOn();
16285 void RequestQuitGame(boolean ask_if_really_quit)
16287 boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
16288 boolean skip_request = AllPlayersGone || quick_quit;
16290 RequestQuitGameExt(skip_request, quick_quit,
16291 "Do you really want to quit the game ?");
16295 /* ------------------------------------------------------------------------- */
16296 /* random generator functions */
16297 /* ------------------------------------------------------------------------- */
16299 unsigned int InitEngineRandom_RND(long seed)
16301 game.num_random_calls = 0;
16304 unsigned int rnd_seed = InitEngineRandom(seed);
16306 printf("::: START RND: %d\n", rnd_seed);
16311 return InitEngineRandom(seed);
16317 unsigned int RND(int max)
16321 game.num_random_calls++;
16323 return GetEngineRandom(max);
16330 /* ------------------------------------------------------------------------- */
16331 /* game engine snapshot handling functions */
16332 /* ------------------------------------------------------------------------- */
16334 #define ARGS_ADDRESS_AND_SIZEOF(x) (&(x)), (sizeof(x))
16336 struct EngineSnapshotInfo
16338 /* runtime values for custom element collect score */
16339 int collect_score[NUM_CUSTOM_ELEMENTS];
16341 /* runtime values for group element choice position */
16342 int choice_pos[NUM_GROUP_ELEMENTS];
16344 /* runtime values for belt position animations */
16345 int belt_graphic[4 * NUM_BELT_PARTS];
16346 int belt_anim_mode[4 * NUM_BELT_PARTS];
16349 struct EngineSnapshotNodeInfo
16356 static struct EngineSnapshotInfo engine_snapshot_rnd;
16357 static ListNode *engine_snapshot_list = NULL;
16358 static char *snapshot_level_identifier = NULL;
16359 static int snapshot_level_nr = -1;
16361 void FreeEngineSnapshot()
16363 while (engine_snapshot_list != NULL)
16364 deleteNodeFromList(&engine_snapshot_list, engine_snapshot_list->key,
16367 setString(&snapshot_level_identifier, NULL);
16368 snapshot_level_nr = -1;
16371 static void SaveEngineSnapshotValues_RND()
16373 static int belt_base_active_element[4] =
16375 EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16376 EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16377 EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16378 EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16382 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16384 int element = EL_CUSTOM_START + i;
16386 engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16389 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16391 int element = EL_GROUP_START + i;
16393 engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16396 for (i = 0; i < 4; i++)
16398 for (j = 0; j < NUM_BELT_PARTS; j++)
16400 int element = belt_base_active_element[i] + j;
16401 int graphic = el2img(element);
16402 int anim_mode = graphic_info[graphic].anim_mode;
16404 engine_snapshot_rnd.belt_graphic[i * 4 + j] = graphic;
16405 engine_snapshot_rnd.belt_anim_mode[i * 4 + j] = anim_mode;
16410 static void LoadEngineSnapshotValues_RND()
16412 unsigned long num_random_calls = game.num_random_calls;
16415 for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16417 int element = EL_CUSTOM_START + i;
16419 element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16422 for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16424 int element = EL_GROUP_START + i;
16426 element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16429 for (i = 0; i < 4; i++)
16431 for (j = 0; j < NUM_BELT_PARTS; j++)
16433 int graphic = engine_snapshot_rnd.belt_graphic[i * 4 + j];
16434 int anim_mode = engine_snapshot_rnd.belt_anim_mode[i * 4 + j];
16436 graphic_info[graphic].anim_mode = anim_mode;
16440 if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16442 InitRND(tape.random_seed);
16443 for (i = 0; i < num_random_calls; i++)
16447 if (game.num_random_calls != num_random_calls)
16449 Error(ERR_INFO, "number of random calls out of sync");
16450 Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
16451 Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
16452 Error(ERR_EXIT, "this should not happen -- please debug");
16456 static void SaveEngineSnapshotBuffer(void *buffer, int size)
16458 struct EngineSnapshotNodeInfo *bi =
16459 checked_calloc(sizeof(struct EngineSnapshotNodeInfo));
16461 bi->buffer_orig = buffer;
16462 bi->buffer_copy = checked_malloc(size);
16465 memcpy(bi->buffer_copy, buffer, size);
16467 addNodeToList(&engine_snapshot_list, NULL, bi);
16470 void SaveEngineSnapshot()
16472 FreeEngineSnapshot(); /* free previous snapshot, if needed */
16474 if (level_editor_test_game) /* do not save snapshots from editor */
16477 /* copy some special values to a structure better suited for the snapshot */
16479 SaveEngineSnapshotValues_RND();
16480 SaveEngineSnapshotValues_EM();
16482 /* save values stored in special snapshot structure */
16484 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16485 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16487 /* save further RND engine values */
16489 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(stored_player));
16490 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(game));
16491 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(tape));
16493 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZX));
16494 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ZY));
16495 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitX));
16496 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExitY));
16498 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16499 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16500 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16501 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16502 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16504 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16505 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16506 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16508 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16510 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
16512 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16513 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16515 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Feld));
16516 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovPos));
16517 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDir));
16518 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16519 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16520 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16521 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16522 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store));
16523 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Store2));
16524 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16525 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Back));
16526 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16527 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16528 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16529 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16530 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16531 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Stop));
16532 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(Pushed));
16534 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16535 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16537 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16538 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16539 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16541 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16542 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16544 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16545 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16546 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16547 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16548 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16550 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16551 SaveEngineSnapshotBuffer(ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16553 /* save level identification information */
16555 setString(&snapshot_level_identifier, leveldir_current->identifier);
16556 snapshot_level_nr = level_nr;
16559 ListNode *node = engine_snapshot_list;
16562 while (node != NULL)
16564 num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16569 printf("::: size of engine snapshot: %d bytes\n", num_bytes);
16573 static void LoadEngineSnapshotBuffer(struct EngineSnapshotNodeInfo *bi)
16575 memcpy(bi->buffer_orig, bi->buffer_copy, bi->size);
16578 void LoadEngineSnapshot()
16580 ListNode *node = engine_snapshot_list;
16582 if (engine_snapshot_list == NULL)
16585 while (node != NULL)
16587 LoadEngineSnapshotBuffer((struct EngineSnapshotNodeInfo *)node->content);
16592 /* restore special values from snapshot structure */
16594 LoadEngineSnapshotValues_RND();
16595 LoadEngineSnapshotValues_EM();
16598 boolean CheckEngineSnapshot()
16600 return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16601 snapshot_level_nr == level_nr);
16605 /* ---------- new game button stuff ---------------------------------------- */
16607 /* graphic position values for game buttons */
16608 #define GAME_BUTTON_XSIZE 30
16609 #define GAME_BUTTON_YSIZE 30
16610 #define GAME_BUTTON_XPOS 5
16611 #define GAME_BUTTON_YPOS 215
16612 #define SOUND_BUTTON_XPOS 5
16613 #define SOUND_BUTTON_YPOS (GAME_BUTTON_YPOS + GAME_BUTTON_YSIZE)
16615 #define GAME_BUTTON_STOP_XPOS (GAME_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16616 #define GAME_BUTTON_PAUSE_XPOS (GAME_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16617 #define GAME_BUTTON_PLAY_XPOS (GAME_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16618 #define SOUND_BUTTON_MUSIC_XPOS (SOUND_BUTTON_XPOS + 0 * GAME_BUTTON_XSIZE)
16619 #define SOUND_BUTTON_LOOPS_XPOS (SOUND_BUTTON_XPOS + 1 * GAME_BUTTON_XSIZE)
16620 #define SOUND_BUTTON_SIMPLE_XPOS (SOUND_BUTTON_XPOS + 2 * GAME_BUTTON_XSIZE)
16628 } gamebutton_info[NUM_GAME_BUTTONS] =
16632 &game.button.stop.x, &game.button.stop.y,
16633 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16638 &game.button.pause.x, &game.button.pause.y,
16639 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16640 GAME_CTRL_ID_PAUSE,
16644 &game.button.play.x, &game.button.play.y,
16645 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16650 &game.button.sound_music.x, &game.button.sound_music.y,
16651 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16652 SOUND_CTRL_ID_MUSIC,
16653 "background music on/off"
16656 &game.button.sound_loops.x, &game.button.sound_loops.y,
16657 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16658 SOUND_CTRL_ID_LOOPS,
16659 "sound loops on/off"
16662 &game.button.sound_simple.x,&game.button.sound_simple.y,
16663 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16664 SOUND_CTRL_ID_SIMPLE,
16665 "normal sounds on/off"
16669 GAME_BUTTON_STOP_XPOS, GAME_BUTTON_YPOS,
16674 GAME_BUTTON_PAUSE_XPOS, GAME_BUTTON_YPOS,
16675 GAME_CTRL_ID_PAUSE,
16679 GAME_BUTTON_PLAY_XPOS, GAME_BUTTON_YPOS,
16684 SOUND_BUTTON_MUSIC_XPOS, SOUND_BUTTON_YPOS,
16685 SOUND_CTRL_ID_MUSIC,
16686 "background music on/off"
16689 SOUND_BUTTON_LOOPS_XPOS, SOUND_BUTTON_YPOS,
16690 SOUND_CTRL_ID_LOOPS,
16691 "sound loops on/off"
16694 SOUND_BUTTON_SIMPLE_XPOS, SOUND_BUTTON_YPOS,
16695 SOUND_CTRL_ID_SIMPLE,
16696 "normal sounds on/off"
16701 void CreateGameButtons()
16705 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16707 Bitmap *gd_bitmap = graphic_info[IMG_GLOBAL_DOOR].bitmap;
16708 struct GadgetInfo *gi;
16711 unsigned long event_mask;
16713 int gd_xoffset, gd_yoffset;
16714 int gd_x1, gd_x2, gd_y1, gd_y2;
16717 x = DX + *gamebutton_info[i].x;
16718 y = DY + *gamebutton_info[i].y;
16719 gd_xoffset = gamebutton_info[i].gd_x;
16720 gd_yoffset = gamebutton_info[i].gd_y;
16721 gd_x1 = DOOR_GFX_PAGEX4 + gd_xoffset;
16722 gd_x2 = DOOR_GFX_PAGEX3 + gd_xoffset;
16724 if (id == GAME_CTRL_ID_STOP ||
16725 id == GAME_CTRL_ID_PAUSE ||
16726 id == GAME_CTRL_ID_PLAY)
16728 button_type = GD_TYPE_NORMAL_BUTTON;
16730 event_mask = GD_EVENT_RELEASED;
16731 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16732 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16736 button_type = GD_TYPE_CHECK_BUTTON;
16738 ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
16739 (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
16740 (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
16741 event_mask = GD_EVENT_PRESSED;
16742 gd_y1 = DOOR_GFX_PAGEY1 + gd_yoffset;
16743 gd_y2 = DOOR_GFX_PAGEY1 + gd_yoffset - GAME_BUTTON_YSIZE;
16746 gi = CreateGadget(GDI_CUSTOM_ID, id,
16747 GDI_INFO_TEXT, gamebutton_info[i].infotext,
16752 GDI_X, DX + gd_xoffset,
16753 GDI_Y, DY + gd_yoffset,
16755 GDI_WIDTH, GAME_BUTTON_XSIZE,
16756 GDI_HEIGHT, GAME_BUTTON_YSIZE,
16757 GDI_TYPE, button_type,
16758 GDI_STATE, GD_BUTTON_UNPRESSED,
16759 GDI_CHECKED, checked,
16760 GDI_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y1,
16761 GDI_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y1,
16762 GDI_ALT_DESIGN_UNPRESSED, gd_bitmap, gd_x1, gd_y2,
16763 GDI_ALT_DESIGN_PRESSED, gd_bitmap, gd_x2, gd_y2,
16764 GDI_DIRECT_DRAW, FALSE,
16765 GDI_EVENT_MASK, event_mask,
16766 GDI_CALLBACK_ACTION, HandleGameButtons,
16770 Error(ERR_EXIT, "cannot create gadget");
16772 game_gadget[id] = gi;
16776 void FreeGameButtons()
16780 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16781 FreeGadget(game_gadget[i]);
16784 static void MapGameButtons()
16788 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16789 MapGadget(game_gadget[i]);
16792 void UnmapGameButtons()
16796 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16797 UnmapGadget(game_gadget[i]);
16800 void RedrawGameButtons()
16804 for (i = 0; i < NUM_GAME_BUTTONS; i++)
16805 RedrawGadget(game_gadget[i]);
16808 static void HandleGameButtons(struct GadgetInfo *gi)
16810 int id = gi->custom_id;
16812 if (game_status != GAME_MODE_PLAYING)
16817 case GAME_CTRL_ID_STOP:
16821 RequestQuitGame(TRUE);
16824 case GAME_CTRL_ID_PAUSE:
16825 if (options.network)
16827 #if defined(NETWORK_AVALIABLE)
16829 SendToServer_ContinuePlaying();
16831 SendToServer_PausePlaying();
16835 TapeTogglePause(TAPE_TOGGLE_MANUAL);
16838 case GAME_CTRL_ID_PLAY:
16841 #if defined(NETWORK_AVALIABLE)
16842 if (options.network)
16843 SendToServer_ContinuePlaying();
16847 tape.pausing = FALSE;
16848 DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
16853 case SOUND_CTRL_ID_MUSIC:
16854 if (setup.sound_music)
16856 setup.sound_music = FALSE;
16859 else if (audio.music_available)
16861 setup.sound = setup.sound_music = TRUE;
16863 SetAudioMode(setup.sound);
16869 case SOUND_CTRL_ID_LOOPS:
16870 if (setup.sound_loops)
16871 setup.sound_loops = FALSE;
16872 else if (audio.loops_available)
16874 setup.sound = setup.sound_loops = TRUE;
16875 SetAudioMode(setup.sound);
16879 case SOUND_CTRL_ID_SIMPLE:
16880 if (setup.sound_simple)
16881 setup.sound_simple = FALSE;
16882 else if (audio.sound_available)
16884 setup.sound = setup.sound_simple = TRUE;
16885 SetAudioMode(setup.sound);